zoukankan      html  css  js  c++  java
  • The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest

    Contest Info


    [Practice Link](https://www.jisuanke.com/contest/3098?view=challenges)
    Solved A B C D E F G H I J K
    9/11 O O - - Ø O Ø O Ø O O
    • O 在比赛中通过
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    A. Attack

    题意:
    (n)个城市,(m)条路可以建造,每条路有边权,现在要使得四对城市之间连通(每对城市连通就行,不用四对相互连通),问建造路的最小代价,如果有共同的路,那么不会重复建造。

    思路:

    • 如果是四对城市互相连通,那么就是裸的斯坦纳树
    • 那么考虑这个问题中不好处理的就是有重复边使得答案更优的情况,怎么避免重复计算贡献?
    • 考虑如果在最优答案中有重复边,那么这条重复边所关联的几对城市放在一起做斯坦纳树,不会增加新的边
    • 那么就暴力组合一下,更新答案即可。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 1010
    #define pii pair <int, int>
    #define fi first
    #define se second
    int n, m;
    int P[4][2];
    vector <vector<pii>> G;
    map <string, int> mp; int cnt;
    int get(string s) {
    	if (mp.find(s) == mp.end()) {
    		return mp[s] = ++cnt;
    	}
    	return mp[s]; 
    }
    
    struct SteinerTree {
    	int st[32], dp[32][1 << 8], endSt;
    	bool vis[32][1 << 8];
    	queue <int> que;	
    	void init(int n, vector <int> &vec) {
    		sort(vec.begin(), vec.end());
    		vec.erase(unique(vec.begin(), vec.end()), vec.end());
    		memset(dp, -1, sizeof dp);
    		memset(vis, 0, sizeof vis);
    		memset(st, 0, sizeof st);
    		endSt = 1;
    		for (auto it : vec) {
    			st[it] = endSt;
    			endSt <<= 1;
    		}
    		for (int i = 1; i <= n; ++i) {
    			dp[i][st[i]] = 0;
    		}
    	}
    	void update(int &a, int x) {
    		a = (a > x || a == -1) ? x : a;
    	}
    	void SPFA(int state) {
    		while (!que.empty()) {
    			int u = que.front(); que.pop();
    			vis[u][state] = false;
    			for (auto it : G[u]) {
    				int v = it.fi, w = it.se, y = st[v] | state;
    				if (dp[v][y] == -1 || dp[v][y] > dp[u][state] + w) {
    					dp[v][y] = dp[u][state] + w;
    					if (y != state || vis[v][state]) 
    						continue;
    					vis[v][state] = true;
    					que.push(v);
    				}
    			}
    		}
    	}
    	int solve() {
    		for (int j = 1; j < endSt; ++j) {
    			for (int i = 1; i <= n; ++i) {
    				if (st[i] && (st[i] & j) == 0) continue;
    				for (int sub = (j - 1) & j; sub; sub = (sub - 1) & j) {
    					int x = st[i] | sub, y = st[i] | (j - sub);
    					if (dp[i][x] != -1 && dp[i][y] != -1) {
    						update(dp[i][j], dp[i][x] + dp[i][y]);
    					}
    				}
    				if (dp[i][j] != -1) {
    					que.push(i), vis[i][j] = true;
    				}
    			}
    			SPFA(j);
    		}
    		int res = 1e9;
    		for (int i = 1; i <= n; ++i) if (dp[i][endSt - 1] != -1) {
    			res = min(res, dp[i][endSt - 1]);
    		}
    		return res;
    	}
    }ST;
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0); cout.tie(0);
    	while (cin >> n >> m) {
    		cnt = 0; mp.clear();
    		G.clear(); G.resize(n + 1);
    		for (int i = 1; i <= n; ++i) {
    			string s; cin >> s;
    			get(s);
    		}
    		for (int i = 1, u, v, w; i <= m; ++i) {
    			string s;
    			cin >> s; u = get(s);
    			cin >> s; v = get(s);
    			cin >> w;
    			G[u].push_back(pii(v, w));
    			G[v].push_back(pii(u, w));
    		}
    		vector <int> vec;
    		for (int i = 0; i < 4; ++i) {
    			string s;
    			cin >> s; P[i][0] = get(s);
    			cin >> s; P[i][1] = get(s); 
    		}
    		int res = 1e9, tmp;
    		//1 + 1 + 1 + 1
    		tmp = 0;
    		for (int i = 0; i < 4; ++i) {
    			vec.clear();
    			vec.push_back(P[i][0]);
    			vec.push_back(P[i][1]);
    			ST.init(n, vec); tmp += ST.solve(); 
    		}
    		res = min(res, tmp);
    		
    		// 3 + 1
    		for (int i = 0; i < 4; ++i) {
    			tmp = 0;
    			vec.clear();
    			vec.push_back(P[i][0]);
    			vec.push_back(P[i][1]);
    			ST.init(n, vec); tmp += ST.solve();
    			vec.clear();
    		   	for (int j = 0; j < 4; ++j)	{
    				if (i == j) continue;
    				vec.push_back(P[j][0]);
    				vec.push_back(P[j][1]);
    			}
    			ST.init(n, vec); tmp += ST.solve(); 
    			res = min(res, tmp);
    		}
    
    		// 2 + 2
    		for (int i = 0; i < 4; ++i) {
    			for (int j = i + 1; j < 4; ++j) {
    				tmp = 0;
    				vec.clear();
    				vec.push_back(P[i][0]);
    				vec.push_back(P[i][1]);
    				vec.push_back(P[j][0]);
    				vec.push_back(P[j][1]);
    				ST.init(n, vec); tmp += ST.solve();
    				vec.clear(); 
    				for (int k = 0; k < 4; ++k) if (k != i && k != j) {
    					vec.push_back(P[k][0]);
    					vec.push_back(P[k][1]);
    				}
    				ST.init(n, vec); tmp += ST.solve();
    				res = min(res, tmp);
    			}
    		}
    
    		// 2 + 1 + 1
    		for (int i = 0; i < 4; ++i) {
    			for (int j = i + 1; j < 4; ++j) {
    				tmp = 0;
    				vec.clear();
    				vec.push_back(P[i][0]);
    				vec.push_back(P[i][1]);
    				vec.push_back(P[j][0]);
    				vec.push_back(P[j][1]);
    				ST.init(n, vec); tmp += ST.solve();
    				for (int k = 0; k < 4; ++k) if (k != i && k != j) {
    					vec.clear();
    					vec.push_back(P[k][0]);
    					vec.push_back(P[k][1]);
    					ST.init(n, vec); tmp += ST.solve();
    				}
    				res = min(res, tmp);
    			}
    		}
    
    		// 4
    		vec.clear();
    		for (int i = 0; i < 4; ++i) {
    			vec.push_back(P[i][0]);
    			vec.push_back(P[i][1]);
    		}
    		ST.init(n, vec);
    		res = min(res, ST.solve());
    		cout << res << "
    ";
    	}
    	return 0;
    }
    

    B. Polynomial

    题意:
    给出一个多项式的第(0)项到第(n)项,询问:

    [egin{eqnarray*} sumlimits_{i = l}^r f(i) mod 9999991 end{eqnarray*} ]

    思路:

    • 考虑一个(n)次多项式的前缀和是一个(n + 1)次的多项式。
    • 插值出第(n + 1)项,就可以再插值出前缀和了

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    #define N 1100
    const ll p = 9999991;
    int n, m;
    ll f[N], fac[N], inv[N];
    ll Inv[10000010];
    
    ll qmod(ll base, ll n) {
    	ll res = 1;
    	while (n) {
    		if (n & 1) {
    			res = res * base % p;
    		}
    		base = base * base % p;
    		n >>= 1;
    	}
    	return res;
    }
    
    ll solve(ll *f, int n, int x) {
    	if (x <= n) return f[x];
    	int t = (n & 1) ? -1 : 1;
    	ll res = 0;
    	ll base = 1;
    	for (int i = 0; i <= n; ++i) base = base * (x - i) % p;
    	for (int i = 0; i <= n; ++i, t *= -1) {
    		res += 1ll * t * f[i] * base % p * inv[n - i] % p * inv[i] % p * Inv[x - i] % p;
    		res = (res + p) % p;
    	}
    	return res;
    }
    
    int main() {
    	fac[0] = 1;
    	for (int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % p;
    	inv[N - 1] = qmod(fac[N - 1], p - 2);
    	for (int i = N - 1; i >= 1; --i) inv[i - 1] = inv[i] * i % p;
    	Inv[1] = 1;
    	for (int i = 2; i < p; ++i) Inv[i] = Inv[p % i] * (p - p / i) % p;
    	int T; scanf("%d", &T);
    	while (T--) {
    		scanf("%d%d", &n, &m);
    		for (int i = 0; i <= n; ++i) scanf("%lld", f + i);
    		f[n + 1] = solve(f, n, n + 1);
    		for (int i = 1; i <= n + 1; ++i) f[i] = (f[i] + f[i - 1]) % p;
    		int l, r;
    		while (m--) {
    			scanf("%d%d", &l, &r);
    			printf("%lld
    ", (solve(f, n + 1, r) - solve(f, n + 1, l - 1) + p) % p);	
    		}
    	}
    	return 0;
    }
    

    E. Interesting Trip

    题意:
    询问有多少条长度为(D)的路径中,满足这条路径上所有点的点权的(gcd > 1)

    思路:
    我们先统计出所有长度为(D)的路径,然后减去点权的(gcd = 1)的路径即为答案。
    怎么统计点权(gcd = 1)的路径?
    我们可以考虑莫比乌斯反演,统计(gcd ;|; d)的路径,即将所有点权为(d)的倍数的点构成的森林中长度为(D)的路径条数。
    然后就是经典的容斥,容斥系数是莫比乌斯函数。
    考虑到点权在(10^4)以下的数的不含有平方因子的因数最多有(32)个,也就是说每个点最多会被放到(32)个森林中,那么所有森林的总点数大概是(O(32n))的。
    那么考虑求定长路径条数可以用基于深度的(dp)解决,那么可以做到(O(n))的合并。
    所以总复杂度为(O(32n))

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    namespace IO {
        const int S=(1<<20)+5;
        //Input Correlation
        char buf[S],*H,*T;
        inline char Get() {
            if(H==T) T=(H=buf)+fread(buf,1,S,stdin);
            if(H==T) return -1;return *H++;
        }
        inline int read() {
            int x=0,fg=1;char c=Get();
            while(!isdigit(c)&&c!='-') c=Get();
            if(c=='-') fg=-1,c=Get();
            while(isdigit(c)) x=x*10+c-'0',c=Get();
            return x*fg;
        }
    }using namespace IO;
    typedef long long ll;
    const int N = 5e5 + 10;
    const int M = 3e4 + 10; 
    int n, D, a[N], pri[M], check[M], mu[M], fa[N], deep[N], vis[N], md[N], hson[N]; ll res, F[N];  
    vector<vector<int>> dvec, fac; 
    struct Edge {int v, nx;}e[N << 1]; int h[N];
    inline void addedge(int u, int v) { e[++*h] = {v, h[u]}; h[u] = *h; } 
    
    void sieve() {
    	fac.clear(); fac.resize(M);
    	for (int i = 1; i < M; ++i) 
    		for (int j = i; j < M; j += i)
    			fac[j].push_back(i);
    	*pri = 0;
    	mu[1] = 1;
    	for (int i = 2; i < M; ++i) {
    		if (!check[i]) {
    			pri[++*pri] = i;
    			mu[i] = -1;
    		}
    		for (int j = 1; j <= *pri; ++j) {
    			if (i * pri[j] >= M) break;
    			check[i * pri[j]] = 1;
    			if (i % pri[j] == 0) break;
    			else mu[i * pri[j]] = -mu[i];
    		}
    	}
    }
    
    void pre(int u) {
    	for (int i = h[u]; i; i = e[i].nx) {
    		int v = e[i].v;
    		if (v == fa[u]) continue;	
    		deep[v] = deep[u] + 1;
    		fa[v] = u;
    		pre(v);  
    	}
    	for (auto &it : fac[a[u]])
    		dvec[it].push_back(u);
    }
    
    int tmp[N << 2], *f[N], *id = tmp;
    void getdeep(int u) {
    	md[u] = deep[u];
    	hson[u] = 0;
    	for (int i = h[u]; i; i = e[i].nx) {
    		int v = e[i].v;
    		deep[v] = deep[u] + 1;
    		getdeep(v);
    		if (!hson[u] || md[v] > md[hson[u]]) hson[u] = v;
    	}
    	md[u] = md[hson[u]] + 1;
    }
    
    void dfs(int u) {
    	if (hson[u]) {
    		int v = hson[u];
    		f[v] = f[u] + 1;
    		dfs(v);
    	}
    	f[u][0] = 1;
    	if (md[u] > D) {
    		res += f[u][D];
    	}
    	for (int i = h[u]; i; i = e[i].nx) {
    		int v = e[i].v;
    		if (v == hson[u]) continue;
    		f[v] = id; id += md[v] + 1;
    		dfs(v);
    		for (int i = 0; i < md[v]; ++i) {
    			if (md[u] > D - i - 1 && D - i - 1 >= 0) { 
    				res += 1ll * f[u][D - i - 1] * f[v][i];
    			} 
    		}
    		for (int i = 0; i < md[v]; ++i)
    			f[u][i + 1] += f[v][i];
    	}
    }
    
    inline void gao(vector <int> &vec, int x) {
    	res = 0; h[0] = 0;
    	for (auto &u : vec) h[u] = 0, vis[u] = x;
    	for (auto &u : vec) {
    		if (vis[fa[u]] == x) {
    			addedge(fa[u], u);
    		} else {
    			deep[u] = 0;
    			getdeep(u);
    			id = tmp;
    			f[u] = id;
    			id += md[u] + 1; 
    			dfs(u);
    		}
    	}
    }
    
    int main() {
    	sieve();
    	int _T; _T = read();
    	for (int kase = 1; kase <= _T; ++kase) {
    		printf("Case #%d: ", kase);
    		n = read(); D = read(); 
    		dvec.clear(); dvec.resize(M);  
    		memset(vis, 0, sizeof vis);
    		memset(h, 0, sizeof h);
    		for (int i = 1; i <= n; ++i) a[i] = read();
    		for (int i = 1, u, v; i < n; ++i) {
    			u = read(); v = read();
    			addedge(u, v);
    			addedge(v, u);
    		} 
    		fa[1] = 0; deep[1] = 0;
    		pre(1); 	
    		for (int i = 1; i <= 30000; ++i) if (mu[i]) {
    			gao(dvec[i], i);
    			F[i] = res;
    		}
    		res = F[1];
    		for (int i = 1; i <= 30000; ++i) if (mu[i])
    			res -= 1ll * mu[i] * F[i];
    		printf("%lld
    ", res * 2);
    	}
    	return 0;
    }
    

    F. Sequence

    题意:
    定义:

    [egin{eqnarray*} f(l, r) &=& oplus_{i = l}^r a_i \ F(l, r) &=& oplus_{i = l}^r oplus_{j = i}^r f(i, j) end{eqnarray*} ]

    给出一个序列(a_i),要求支持两种操作:

    • (a_x)修改成(y)
    • 询问(F(l, r))

    思路:
    我们发现直接去计算这个式子比较不好计算,但是注意到异或的一个性质就是一个数异或偶数次就没了,异或奇数次就是本身。
    那么我们不妨去考虑一个(F(l, r))这个过程最终(a_l, cdots, a_r)这每个数最终异或了多少次。
    打表发现如下规律:

    • (r - l + 1)为偶数那么每个数异或次数都是偶数
    • 否则,(l, l + 2, l + 4, cdots)这些位置上的数异或次数是奇数,其它位置上的数异或次数为偶数
      那么只需要维护两个序列,一个是奇数位置上的前缀和,一个是偶数位置上的前缀和,就可以做了。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 100010
    int n, q, a[N];
    
    struct SEG {
    	int t[N << 2];
    	void init() {
    		memset(t, 0, sizeof t);
    	}
    	void update(int id, int l, int r, int pos, int x) {
    		if (l == r) {
    			t[id] = x;
    			return;
    		}
    		int mid = (l + r) >> 1;
    		if (pos <= mid) update(id << 1, l, mid, pos, x);
    		else update(id << 1 | 1, mid + 1, r, pos, x);
    		t[id] = t[id << 1] ^ t[id << 1 | 1];
    	}
    	int query(int id, int l, int r, int ql, int qr) {
    		if (l >= ql && r <= qr) {
    			return t[id];
    		}
    		int mid = (l + r) >> 1;
    		int x = 0;
    		if (ql <= mid) x ^= query(id << 1, l, mid, ql, qr);
    		if (qr > mid) x ^= query(id << 1 | 1, mid + 1, r, ql, qr);
    		return x;
    	}
    }seg[2];
    
    int main() {
    	int T; scanf("%d", &T);
    	for (int kase = 1; kase <= T; ++kase) {
    		printf("Case #%d:
    ", kase);
    		scanf("%d%d", &n, &q);
    		for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    		seg[0].init(); seg[1].init();
    		for (int i = 1; i <= n; ++i) {
    			seg[i % 2].update(1, 1, n, i, a[i]);
    		}
    		int op, x, y;
    		while (q--) {
    			scanf("%d%d%d", &op, &x, &y);
    			switch(op) {
    				case 0 :
    					seg[x % 2].update(1, 1, n, x, y);
    					break;
    				case 1 :
    					if ((y - x + 1) % 2 == 0) puts("0");
    					else {
    						printf("%d
    ", seg[x % 2].query(1, 1, n, x, y));
    					}
    					break;
    			}
    		}
    	}
    	return 0;
    }
    

    G. Winner

    题意:
    有一种游戏,三个模式,每个玩家在每个模式中都有一个力量值。现在要进行(n - 1)轮游戏,每一轮要挑出两个未被淘汰的人选择一种模式进行决斗,
    在那个模式中,力量值小的人会被淘汰,直到最后剩下一个人。
    数据保证每种模式中不同玩家的力量值不同。
    现在上帝可以选择每一轮游戏参与决斗的两人(未被淘汰的),以及游戏模式,现在询问在上帝的操作下,第(x)个人是否可以成为最后留下来的人?

    思路:
    我们考虑先排个序,然后用((a, b, c))表示要成为最后留下来的人的三种模式的力量的最小值,如果有人有某种大于等于这个三元组中对应的那个,那么就可以用它的其他两个属性去更新这个三元组。
    按从大到小的顺序去更新即可。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 100010 
    #define pii pair <int, int>
    #define fi first
    #define se second
    int n, q;
    struct node {
    	int a, b, c;
    	node() {}
    	node(int a, int b, int c) : a(a), b(b), c(c) {}
    	bool operator <= (const node &other) const {
    		return a <= other.a || b <= other.b || c <= other.c;
    	}
    }p[N];
    pii a[N], b[N], c[N];
    
    void Min(node &x, node y) {
    	x.a = min(x.a, y.a);
    	x.b = min(x.b, y.b);
    	x.c = min(x.c, y.c);
    }
    
    int main() {
    	while (scanf("%d%d", &n, &q) != EOF) {
    		for (int i = 1; i <= n; ++i) {
    			scanf("%d", &a[i].fi);
    			a[i].se = i;
    			p[i].a = a[i].fi;
    		}
    		for (int i = 1; i <= n; ++i) {
    			scanf("%d", &b[i].fi);
    			b[i].se = i;
    			p[i].b = b[i].fi;
    		}
    		for (int i = 1; i <= n; ++i) {
    			scanf("%d", &c[i].fi);
    			c[i].se = i;
    			p[i].c = c[i].fi;
    		}
    		sort(a + 1, a + 1 + n);
    		sort(b + 1, b + 1 + n);
    		sort(c + 1, c + 1 + n);
    		node T = node(1e9, 1e9, 1e9);
    		Min(T, p[a[n].se]);
    		Min(T, p[b[n].se]);
    		Min(T, p[c[n].se]);
    		for (int i = n - 1; i >= 1; --i) {
    			int pos = a[i].se;
    			if (T <= p[pos]) {
    				Min(T, p[pos]);
    			}
    			pos = b[i].se;
    			if (T <= p[pos]) {
    				Min(T, p[pos]);
    			}
    			pos = c[i].se;
    			if (T <= p[pos]) {
    				Min(T, p[pos]);
    			}
    		}
    		int x;
    		while (q--) {
    			scanf("%d", &x);
    			puts(T <= p[x] ? "YES" : "NO"); 
    		}
    	} 
    	return 0;
    }
    

    H. Another Sequence

    题意:
    给出一个长度为(n)的序列(a_i)和另一个长度为(n)的序列(b_i),现在(c_i)的生成方式如下:

    • (c_i)的长度为(n * n)
    • (c_{i * (n - 1) + j} = a_i | b_j)
    • (c_i)升序排序
      现在要求支持两种操作:
    • (c_i)([l, r])区间内的数开根
    • 询问(c_x)的数值。

    思路:

    • 对于(c_i)数组,我们肯定不能直接生成它,但是注意到(1 leq a_i, b_i leq 10^5),那么(c_i)中数的种类不会超过(2 cdot 10^5),那么我们可以用(FWT_or)处理出每个数的个数
    • 那么对于询问,我们只需要处理出(x)位置上的数被开根了多少次,然后找到原本(x)位置上的数是什么即可
    • 我们考虑(x)位置上的数被开根了多少次?即在它前面出现的操作(1)中,(l leq x)的区间个数减去(r < x)的区间个数
    • 考虑怎么找到(x)位置上的数是什么,维护一个前缀和,表示前(i)个数的出现次数,然后直接二分。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    #define N 200010
    int n, q, a[N], b[N];
    ll c[N], d[N];
    ll H[N << 1]; int tot;
    int res[N]; 
    vector <vector<ll>> vec;
    struct node {
    	ll x, y;
    	node() {}
    	node (ll x, ll y) : x(x), y(y) {}
    }qrr[N];
    
    void FWT(ll *a, int len, int mode) {
    	for (int i = 1; i < len; i <<= 1) {
    		for (int p = i << 1, j = 0; j < len; j += p) {
    			for (int k = 0; k < i; ++k) {
    				if (mode == 1) a[i + j + k] = (a[j + k] + a[i + j + k]);
    				else a[i + j + k] = (a[i + j + k] - a[j + k]);
    			}
    		}
    	}
    }
    
    struct BIT {
    	int a[N];
    	void init() {
    		memset(a, 0, sizeof a);
    	}
    	void update(int x, int v) {
    		for (; x < N; x += x & -x) {
    			a[x] += v;
    		}
    	}
    	int query(int x) {
    		int res = 0;
    		for (; x > 0; x -= x & -x) {
    			res += a[x];
    		}
    		return res;
    	}
    }bit[2];
    
    int id(ll x) {
    	return lower_bound(H + 1, H + 1 + tot, x) - H;
    }
    
    int main() {
    	int len = 1;
    	while (len < 100000) len <<= 1;
    	vec.clear(); vec.resize(len + 1);
    	vec[0].push_back(0); vec[1].push_back(1);
    	for (int i = 2; i <= len; ++i) {
    		int it = i;
    		vec[i].push_back(it); 
    		while (it) {
    			it = floor(sqrt(it));
    			vec[i].push_back(it);
    			if (it == 1) break;
    		}
    		
    	}	
    	while (scanf("%d", &n) != EOF) {
    		for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    		for (int i = 1; i <= n; ++i) scanf("%d", b + i);
    		memset(c, 0, sizeof c);
    		memset(d, 0, sizeof d);
    		for (int i = 1; i <= n; ++i) ++c[a[i]], ++d[b[i]];
    		FWT(c, len, 1);
    		FWT(d, len, 1);
    		for (int i = 0; i < len; ++i) c[i] = c[i] * d[i];
    		FWT(c, len, -1);
    		for (int i = 0; i < len; ++i) c[i] += c[i - 1];
    		scanf("%d", &q);
    		tot = 0;
    		ll x, y;
    		for (int i = 1; i <= q; ++i) {
    			scanf("%lld%lld", &x, &y);
    			H[++tot] = x;
    			H[++tot] = y;
    			qrr[i] = node(x, y);
    			res[i] = -1;   
    		}
    		sort(H + 1, H + 1 + tot);
    		tot = unique(H + 1, H + 1 + tot) - H - 1;
    		bit[0].init(); bit[1].init();
    		for (int i = 1; i <= q; ++i) {
    			if (qrr[i].x == 0) {
    				int del = bit[0].query(id(qrr[i].y)) - bit[1].query(id(qrr[i].y) - 1); 
    				int num = lower_bound(c + 1, c + len, qrr[i].y) - c;
    				del = min(del, (int)vec[num].size() - 1);
    				res[i] = vec[num][del];
    			} else {
    				bit[0].update(id(qrr[i].x), 1);
    				bit[1].update(id(qrr[i].y), 1);
    			}
    		}
    		for (int i = 1; i <= q; ++i) if (res[i] != -1) printf("%d
    ", res[i]);
    	}
    	return 0;
    }
    

    I. Hamster Sort

    题意:
    给出一个排列(p_i), 给定一种排序操作:

    1. 选择一个(k),令(p_k = 1)(T = 1)
    2. 然后遍历序列(a_i),寻找(p_k)
    3. 如果找到了,就令(p_k = p_k + k),遍历指针往后挪一个位置,然后回到步骤(2)
    4. 如果没有找到,就令(T = T + 1),然后遍历指针指向序列的开头,回到步骤(2)

    现在要支出两种操作:

    • 交换(p_x)(p_y)
    • 给出(k),询问上述排序操作的(T)是多少

    思路:

    • 考虑(k > sqrt{n})的时候,我们可以直接暴力跳,不会跳超过(sqrt{n})
    • 考虑(k leq sqrt{n})的时候,(k)的取值只有(sqrt{n})种,可以直接预处理答案,交换的时候注意一下(a_x)(a_y)都是同一个(k)的情况的贡献不要多加或者多减
    • 预处理答案的时候把(1)的贡献剔除出来,最后再加上即可

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define N 200010
    int n, q, S;
    int a[N], id[N];
    int f[N];
    
    int main() {
    	int T; scanf("%d", &T); 
    	while (T--) {
    		scanf("%d", &n); S = min(n - 1, 200);  
    		for (int i = 1; i <= n; ++i) scanf("%d", a + i), id[a[i]] = i;
    		memset(f, 0, sizeof f);
    		for (int i = 1; i <= n; ++i) {
    			for (int j = 1; j <= S; ++j) {
    				if (a[i] > 1 && (a[i] - 1) % j == 0) {
    					 if (id[a[i] - j] > i) {
    					 	++f[j];
    					 }
    				}
    			}
    		}
    		scanf("%d", &q);
    		int op, x, y, k;
    		while (q--) {
    			scanf("%d", &op);
    			if (op == 1) {
    				scanf("%d%d", &x, &y);
    				if (x == y) continue; 
    				if (x > y) swap(x, y); 
    				for (int i = 1; i <= S; ++i) {
    					if ((a[x] - 1) % i == 0) {
    						int pre = a[x] - i;
    						if (pre >= 1 && id[pre] <= y && id[pre] > x) {
    							--f[i];
    						} 
    						int nx = a[x] + i;
    						if (nx <= n && id[nx] <= y && id[nx] > x) {
    							++f[i]; 
    						}
    					}
    				}	
    				for (int i = 1; i <= S; ++i) {
    					if ((a[y] - 1) % i == 0) {
    						int pre = a[y] - i;
    						if (pre >= 1 && id[pre] > x && id[pre] < y) {
    							++f[i];
    						}	
    						int nx = a[y] + i;
    						if (nx <= n && id[nx] > x && id[nx] < y) {
    							--f[i];
    						}
    					}
    				}
    				swap(id[a[x]], id[a[y]]);
    				swap(a[x], a[y]);
    			} else {
    				scanf("%d", &k);
    				if (k <= S) {
    					printf("%d
    ", f[k] + 1); 
    				} else {
    					int res = 1; 
    					int it = 1;
    					while (it + k <= n) {
    						if (id[it + k] < id[it]) ++res;
    						it += k;
    					}
    					printf("%d
    ", res);  
    				}	
    			}
    		}
    	}
    	return 0;
    }
    

    J. Prefix

    题意:
    给出(n)个字符串(s_i)

    • 维护一个字符串集合,把这个字符串加进去。
    • 再将所有字符串替换成他们的(|s_i|)个前缀。
      定义一个字符串的困难度为:

    [egin{eqnarray*} prodlimits_{i = 1}^{|str|} d_{str_i} mod m end{eqnarray*} ]

    现在询问每个原字符串在那个字符串集合中,有多少字符串是它的前缀,并且困难度比它高?

    思路:
    先将所有字符串(Hash),存入(map)
    然后遍历每个字符串的前缀,如果前缀的困难度比它本身高,那么就统计有字符串集合中有多少个这样的前缀。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    #define N 100010
    #define ull unsigned long long 
    int n, m, d[30], a[N];
    string s[N]; 
    
    map<ull, int> mp;
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0); cout.tie(0); 
    	while (cin >> n >> m) {
    		mp.clear();
    		for (int i = 0; i < 26; ++i) cin >> d[i];
    		for (int i = 1; i <= n; ++i) cin >> s[i];
    		for (int i = 1; i <= n; ++i) {
    			ull Hash = 0;
    			ull base = 31;
    			a[i] = 1;
    			for (auto it : s[i]) {
    				Hash += base * it;
    				++mp[Hash]; 
    				base *= 31;
    				a[i] = 1ll * a[i] * d[it - 'a'] % m;
    			}
    		}
    		for (int i = 1; i <= n; ++i) {
    			ull Hash = 0;
    			ull	base = 31;
    			int tmp = 1;
    			ll res = 0;
    			for (int j = 0, len = s[i].size(); j < len - 1; ++j) {
    				Hash += base * s[i][j];
    				base *= 31;
    				tmp = 1ll * tmp * d[s[i][j] - 'a'] % m;
    				if (tmp > a[i]) res += mp[Hash];
    			}
    			printf("%lld%c", res, " 
    "[i == n]);	
    		}
    	}
    	return 0;
    }
    

    K. A Good Game

    签到题。
    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    #define N 100010
    int n, m;
    ll v[N];
    
    int main() {
    	int T; scanf("%d", &T);
    	while (T--) {
    		scanf("%d%d", &n, &m);
    		for (int i = 1; i <= n; ++i) {
    			scanf("%lld", v + i);
    			v[i] += v[i - 1];
    		}
    		ll res = 0;
    		vector <ll> vec;
    		for (int i = 1, l, r; i <= m; ++i) {
    			scanf("%d%d", &l, &r);
    			vec.push_back(v[r] - v[l - 1]);
    		}
    		sort(vec.begin(), vec.end());
    		for (int i = 0; i < m; ++i) {
    			res += 1ll * (i + 1) * vec[i];
    		}
    		printf("%lld
    ", res);
    	}
    	return 0;
    }
    
  • 相关阅读:
    放大镜/鼠标移入放大
    窗口拖动
    call、apply、bind三者的区别
    window下jdk配置
    Linux jdk+tomcat+mysql 安装及配置
    linux下yum包更新不了
    如何让form2中的数据源,显示在form1的dataGridView控件中呢????
    自己写的SqlHelper,提示在调用"Fill"前,SelectCommand 属性尚未初始化.错误
    datatable和dataset的区别
    c# 数据库基础(将连接字符串写到配置文件中)
  • 原文地址:https://www.cnblogs.com/Dup4/p/11235580.html
Copyright © 2011-2022 走看看