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 << "\n";
	}
	return 0;
}

B. Polynomial

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

\[\begin{eqnarray*} \sum\limits_{i = l}^r f(i) \bmod 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\n", (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\n", res * 2);
	}
	return 0;
}

F. Sequence

题意:
定义:

\[\begin{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:\n", 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\n", 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\n", 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\n", 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\n", res);  
				}	
			}
		}
	}
	return 0;
}

J. Prefix

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

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

\[\begin{eqnarray*} \prod\limits_{i = 1}^{|str|} d_{str_i} \bmod 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, " \n"[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\n", res);
	}
	return 0;
}
posted @ 2019-07-24 07:39  Dup4  阅读(598)  评论(0编辑  收藏  举报