2018 Multi-University Training Contest 3

传送门

A - Problem A. Ascending Rating

题意:
对于所有长度为\(k\)的区间,分别需要求出区间最大值以及更新得到区间最大值的次数。

思路:

  • 一开始正着考虑,写了个并查集,想着卡卡常数可以过,结果直接T飞。。。
  • 当时想难点就是随着区间的移动,每次都会删去区间左端点的点然后加入一个点,一开始我们求答案的时候可能会略去新加的这个点所以之后会重新算;
  • 但是如果直接倒着来考虑,发现每次都是新加一个点啦,那么直接用单调队列搞搞就行。
  • 观察能得到单调队列维护的递减序列中的个数,就是需要更新的次数。

挺巧妙的,主要是删点和加点顺序不同时,两者可以转换。当然也可以直接大力观察到单调队列递减序列更新过程也是更新最大值的过程。。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e7 + 5;

int n, m, k, p, q, r, MOD;
int a[N], que[N];

void run() {
	cin >> n >> m >> k >> p >> q >> r >> MOD;
	for(int i = 1; i <= k; i++) cin >> a[i];
	for(int i = k + 1; i <= n; i++) 
		a[i] = (1ll * p * a[i - 1] % MOD + 1ll * q * i % MOD + r) % MOD;
	int L = 1, R = 0;
	for(int i = n; i >= n - m + 1; i--) {
		while(L <= R && a[que[R]] <= a[i]) --R;
		que[++R] = i;
	}
	ll A = a[que[L]] ^ (n - m + 1), B = (R - L + 1) ^ (n- m + 1);
	for(int i = n - m; i >= 1; i--) {
		while(L <= R && que[L] > i + m - 1) ++L;
		while(L <= R && a[que[R]] <= a[i]) --R;
		que[++R] = i;
		A += a[que[L]] ^ i, B += (R - L + 1) ^ i;
	}
	cout << A << ' ' << B << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;
}

C - Problem C. Dynamic Graph Matching

题意:
现有一个点数为\(n,n\leq 10,n\%2=0\)的图,然后有两种操作,一种是添加一条边(可能会产生重边);另一种是删掉一条已经存在的边。
问每种操作过后,点匹配数为\(2,4,\cdots, n\)的情况有多少种。

思路:

  • 如果考虑只有加边操作,那么可以考虑状压,将已经匹配的点的所有情况压一下。
  • 因为一条边加入的先后顺序不影响答案,所以对于删除操作,可以类似于可删除背包那样撤去贡献。
  • 删边时正着循环来删,因为每次删去的数组中不能含有要删的那条边,这才是那条边的贡献。

详见代码:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 11, MOD = 1e9 + 7;

int n, m;
int dp[1 << N], cnt[1 << N], ans[N];

void init() {
	for(int i = 0; i < 1 << N; i++) {
		cnt[i] = __builtin_popcount(i);
	}
}

void add(int &x, int y) {
	x += y;
	if(x >= MOD) x -= MOD;
}

void sub(int &x, int y) {
	x -= y;
	if(x < 0) x += MOD;
}

void run() {
	cin >> n >> m;
	int lim = 1 << n;
	for(int i = 0; i < lim; i++) {
		dp[i] = 0;
	}
	dp[0] = 1;
	for(int j = 1; j <= m; j++) {
		char c[2]; int x, y;
		cin >> c >> x >> y;
		--x, --y;
		int s = (1 << x) | (1 << y);
		if(c[0] == '+') {
			for(int i = lim - 1; ~i; i--) {
				if(!(s & i)) {
					add(dp[s ^ i], dp[i]);
				}
			}
		} else {
			for(int i = 0; i < lim; i++) {
				if(!(s & i)) {
					sub(dp[s ^ i], dp[i]);
				}
			}
		}
		for(int i = 0; i <= n; i++) ans[i] = 0;
		for(int i = 0; i < lim; i++) add(ans[cnt[i]], dp[i]);
		for(int i = 2; i <= n; i += 2) cout << ans[i] << " \n"[i == n];
	}
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    init();
    int T; cin >> T;
    while(T--) run();
    return 0;
}

D - Problem D. Euler Function

签到。

Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 2e5+5,MAXM = 1e6+5,MOD = 1e9+7,INF = 0x3f3f3f3f,N=2e5;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
#define x first
#define y second
int t,k;
int main(){
    ios::sync_with_stdio(false);
    //freopen("../A.in","r",stdin);
    //freopen("../A.out","w",stdout);
    cin>>t;
    while(t--){
        cin>>k;
        if(k==1)cout<<"5\n";
        else if(k==2)cout<<"7\n";
        else{
            cout<<5+k<<'\n';
        }
    }
    return 0;
}

F - Problem F. Grab The Tree

从高到底依次考虑每个二进制位,发现某位为奇数时,先手必胜;否则考虑下一位。
最差的情况就是平局。

Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 1e6+5,MOD = 1e9+7,INF = 0x3f3f3f3f,N=2e5;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
#define x first
#define y second

int T,cnt[33],w[MAXN],n;
int main(){
    ios::sync_with_stdio(false);
    //freopen("../A.in","r",stdin);
    //freopen("../A.out","w",stdout);
    cin>>T;
    while(T--){
        cin>>n;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++)cin>>w[i];
for(int i = 1; i < n; i++) {int u, v; cin >> u >> v;}
        for(int i=1;i<=n;i++){
            for(int j=0;j<32;j++){
                if(w[i]>>j&1)cnt[j]++;
            }
        }
        bool ok=false;
        for(int i=31;i>=0;i--){
            if(cnt[i]&1){
                ok=true;
                break;
            }
        }
        if(ok)cout<<"Q\n";
        else cout<<"D\n";
    }
    return 0;
}

G - Problem G. Interstellar Travel

比较裸的维护凸包,注意一下顺时针多边形面积为负就行。
另外还要注意一下要求字典序最小,所以一开始排序的时候要自定义规则,共线的情况也需要注意处理。
详细见代码:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5;

int n;
struct Point{
	int x, y, id;
	bool operator < (const Point &A) const {
		if(x != A.x) return x < A.x;
		if(y != A.y) return y > A.y;
		return id < A.id;
	}
}p[N];

int q[N], f[N];
Point tmp[N];
bool must[N];

void run() {
	cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> p[i].x >> p[i].y;
		p[i].id = i;
		must[i] = false;
	}
	sort(p + 1, p + n + 1);
	int r = 0;
	for(int i = 1; i <= n; i++) {
		if(i > 1 && p[i].x == p[i - 1].x) continue;
		while(r > 1 && 1ll * (p[q[r]].y - p[q[r - 1]].y) * (p[i].x - p[q[r]].x) < 
			1ll * (p[i].y - p[q[r]].y) * (p[q[r]].x - p[q[r - 1]].x)
			) --r;
		q[++r] = i;
	}
	for(int i = 1; i <= r; i++) {
		tmp[i] = p[q[i]];
	}
	for(int i = 2; i < r; i++) {
		if(1ll * (tmp[i + 1].y - tmp[i].y) * (tmp[i].x - tmp[i - 1].x) != 
			1ll * (tmp[i].y - tmp[i - 1].y) * (tmp[i + 1].x - tmp[i].x)
			) must[i] = 1;
	}
	must[1] = must[r] = 1;
	for(int i = r; i >= 1; i--) {
		if(!must[i]) {
			f[i] = min(tmp[i].id, f[i + 1]);
		} else {
			f[i] = tmp[i].id;
		}
	}
	for(int i = 1; i <= r; i++) {
		if(f[i] == tmp[i].id) cout << f[i] << " \n"[i == r];
	}
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;
}

I - Problem I. Random Sequence

题意:
给出一个长度为\(n\)的序列\(a\),若\(a_i=0\),那么\(a_i\)的值为\([1,m]\)中随机一个数。
最后问

\[\prod_{i=1}^{n-3}v[gcd(a_i,a_{i+1},a_{i+2},a_{i+3})] \]

的期望值是多少。
\(v\)数组会给出。

思路:

  • 比较直接的一种想法为\(dp(or\)递推\(?)\)来求期望值:\(dp[i][x][y][z]\)表示前\(i\)个位置,最后三个分别为\(x,y,z\)的期望值是多少,那么直接枚举下一位转移即可。
  • 但是时间复杂度不能承受。
  • 因为最终要求的是\(gcd\),假设\(z=3\),那么\(x,y\)虽然有很多的取值,但最后的答案只可能有两个,所以我们其实不用关心\(x,y\)具体的取值,转而关心对应的\(gcd\)为多少就行。
  • 所以\(x=gcd(a_{i-2},a_{i-1},a_i),y=gcd(a_{i-1},a_i),z=a_i\),同样地枚举下一位进行转移就行。
  • 最后满足\(x|y,y|z\),所以状态数就变少了。

感觉做法类似于状态压缩的思想?把许多无用的状态略去,同时定义一个好的状态能够表示所有的情况...emmm还是挺神奇的。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 105, MOD =1e9 + 7;

int n, m;
int a[N], v[N];
int dp[2][N][N][N];

int g[N][N];

ll qpow(ll a, ll b) {
	ll ans = 1;
	while(b) {
		if(b & 1) ans = ans * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return ans;
}

void run() {
	cin >> n >> m;
	for(int i = 1; i <= n; i++) cin >> a[i];
	for(int i = 1; i <= m; i++) cin >> v[i];
	for(int i = 0; i <= m; i++) {
		for(int j = 0; j <= m; j++) {
			g[i][j] = __gcd(i, j);
		}
	}
	memset(dp, 0, sizeof(dp));
	int cur = 0;
	for(int i = 1; i <= m; i++) {
		if(a[1] && i != a[1]) continue;
		for(int j = 1; j <= m; j++) {
			if(a[2] && j != a[2]) continue;
			for(int k = 1; k <= m; k++) {
				if(a[3] && k != a[3]) continue;
				// i j k
				++dp[cur][g[g[j][k]][i]][g[j][k]][k];
				if(dp[cur][g[g[j][k]][i]][g[j][k]][k] >= MOD)
					dp[cur][g[g[j][k]][i]][g[j][k]][k] -= MOD;
			}
		}
	}
	for(int i = 3; i < n; i++, cur ^= 1) {
		for(int j = 1; j <= m; j++) {
			for(int k = j; k <= m; k += j) {
				for(int w = k; w <= m; w += k) {
					//j, k, w
					for(int p = 1; p <= m; p++) {
						if(a[i + 1] && a[i + 1] != p) continue;
						dp[cur ^ 1][g[k][p]][g[p][w]][p] += 1ll * dp[cur][j][k][w] * v[g[j][p]] % MOD;
						dp[cur ^ 1][g[k][p]][g[p][w]][p] %= MOD;
					}
					dp[cur][j][k][w] = 0;
				}
			}
		}
	}
	int cnt = 0;
	for(int i = 1; i <= n; i++) if(!a[i]) ++cnt;
	int ans = 0;
	for(int i = 1; i <= m; i++) {
		for(int j = i; j <= m; j += i) {
			for(int k = j; k <= m; k += j) {
				ans += dp[cur][i][j][k];
				if(ans >= MOD) ans -= MOD;
			}
		}
	}
	ans = 1ll * ans * qpow(qpow(m, cnt), MOD - 2) % MOD;
	cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;
}

L - Problem L. Visual Cube

递归求解即可。

Code
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
char ans[47*5][47*5];
void draw(int x, int y) {
	static const char cube[][6]={"  +-+"," /./|","+-+.+","|.|/ ","+-+  "};
	REP(i,0,5) REP(j,0,5) {
		if(cube[i][j]!=' ') {
			ans[x+i][y+j]=cube[i][j];
		}
	}
}
int main() {
	int t; scanf("%d", &t);
	while(0<t--) {
		int a,b,c; scanf("%d%d%d", &a, &b, &c);
		int w=2*b+2*a+1;
		int h=2*b+2*c+1;
		REP(i,0,h) REP(j,0,w) { ans[i][j]='.';
		}
		PERE(k,c-1,0)REP(j,0,b)REP(i,0,a) {
			draw(2*j+2*k,(b-j-1)*2+2*i);
		}
		REP(i,0,h) {
			REP(j,0,w) putchar(ans[i][j]);
			putchar('\n');
		}
	}
}

M - Problem M. Walking Plan

题意:
\(q,q\leq 10^5\)组询问,每次回答从\(s\)\(t\)至少经过\(k,k\leq 10000\)条边的最短路径。
\(n\leq 50\)

思路:

  • 考虑一组询问的情况,矩阵乘法求出走\(k,1\leq k\leq 20000\)步任意两点的最短路径,然后两点的后缀最小值即为答案。
  • 但会计算到\(20000\)去,显然不能承受;
  • 注意到在所有的走法中,经过边的数量是递增的,可以直接对\(k\)分块为:\(k=100\cdot x+y\);那么直接预处理出\(100\cdot x,x\leq 100\)的矩阵和\(y,y\leq 200\)的矩阵就行。
  • 一个表示恰好走\(100\cdot x\)步,另一个表示至少走\(y\)步的方案,此时\(y\)较小,可以直接按一开始来说的做。

为啥是\(200\)?因为要求的是至少,所以走\(n+k\)最为保险,但我\(100\)还是过了hhh。

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
// #define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 105;

int n, m;
int f[2 * N][N][N], g[N][N][N];
int t[N][N];

void mul(int a[][N], int b[][N], int c[][N]) {
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			t[i][j] = INF;
		}
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			for(int k = 1; k <= n; k++) {
				t[i][j] = min(t[i][j], a[i][k] + b[k][j]);
			}
		}
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			c[i][j] = min(c[i][j], t[i][j]);
		}
	}
}

void run() {
	memset(f, INF, sizeof(f));
	memset(g, INF, sizeof(g));
	cin >> n >> m;
	for(int i = 1; i <= n; i++) {
		g[0][i][i] = f[0][i][i] = 0;
	}	
	for(int i = 1; i <= m; i++) {
		int u, v, w; cin >> u >> v >> w;
		f[1][u][v] = min(f[1][u][v], (int)w);
	}
	for(int i = 2; i < 2 * N; i++) {
		mul(f[i - 1], f[1], f[i]);
	}
	for(int i = 1; i < N; i++) {
		mul(g[i - 1], f[100], g[i]);
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= n; j++) {
			for(int k = 100; k >= 0; k--) {
				f[k][i][j] = min(f[k][i][j], f[k + 1][i][j]);
			}
		}
	}	
	int q; cin >> q;
	while(q--) {
		int s, t, k; cin >> s >> t >> k;
		int k1 = k / 100, k2 = k % 100;
		int ans = INF;
		for(int i = 1; i <= n; i++) {
			ans = min(ans, g[k1][s][i] + f[k2][i][t]);
		}
		if(ans == INF) ans = -1;
		cout << ans << '\n';
	}
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;
}

posted @ 2019-10-11 21:44  heyuhhh  阅读(197)  评论(0编辑  收藏  举报