做题记录 3

Underground Lab

来源:CF780E, 2200  

题目是保证有解的,所以对于任意的一棵树来说也一定有解,那就不妨思考树的情况.  

这道题最苛刻的情况就是 $\mathrm{K=1}$, 即意味着需要遍历完整颗树且中间不能断开.  

很自然想到构建欧拉序,不过长度是 $\mathrm{O(3n)}$ 的,那就魔改一下:每次便利完儿子后不再将 $\mathrm{x}$ 加入,这样就好了.  

#include <bits/stdc++.h>
#define N  400009  
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
struct UFS {
	int p[N]; 
	void init() {
		for(int i = 0; i < N ; ++ i) p[i] = i; 
	}
	int find(int x) {
		return p[x] == x ? x : p[x] = find(p[x]); 
	}
	int merge(int x, int y) {
		x = find(x), y = find(y); 
		if(x == y) return 0; 
		p[x] = y; 
		return 1; 
	}
}T; 
int n, m, K, bu[N], scc;   
vector<int>G[N]; 
void dfs(int x, int ff) {
	bu[++ scc] = x; 
	for(int i = 0; i < G[x].size() ; ++ i) {
		int v = G[x][i]; 
		if(v == ff) continue;   
		dfs(v, x);  
		bu[++ scc] = x;  
	}
}
int main() {
	/// setIO("input");  
	scanf("%d%d%d", &n, &m, &K); 
	T.init(); 
	for(int i = 1; i <= m ; ++ i) {
		int x, y; 
		scanf("%d%d", &x, &y); 
		if(T.merge(x, y)) {
			G[x].pb(y); 
			G[y].pb(x); 
		}
	}
	dfs(1, 0);
	int num = (2 * n + K - 1) / K; 
	int cur = 1;    
	for(int i = 1; i <= K ; ++ i) { 
		if(cur > scc) {
			printf("%d %d\n", 1, 1); 
		}
		else {
			int l = cur, r = min(scc, cur + num - 1);
			printf("%d ", r - l + 1);  
			for(int j = l; j <= r; ++ j) {
				printf("%d ", bu[j]); 
			}
			printf("\n");  
			cur = r + 1; 
		}
	}
	return 0; 
}

  

Send Boxes to Alice (Hard Version)

来源:CF1254B2, 2100  

$K$ 一定是综合的因数,所以不妨考虑枚举这个 $\mathrm{K}$.  

然后显然 $\mathrm{K}$ 是质因数的情况是最好的,因为会更稠密,更容易满足.  

对于一个 $\mathrm{K}$, 令所有数字对 $\mathrm{K}$ 取模,表示需要补齐的部分.   

假设前 $\mathrm{i-1}$ 个数字都补好,就差 $\mathrm{i}$, 那么 $\mathrm{i}$ 差的就是前 $\mathrm{i}$ 个数的和模 $\mathrm{K}$.  

这个用 $\mathrm{i+1}$ 来贡献的话就是 $\mathrm{i}$ 移向 $\mathrm{i+1}$ 或 $\mathrm{i+1}$ 移向 $i$, 但 $\mathrm{i+1}$ 不会变.  

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N  1000009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std;   
ll b[N], a[N], sum, ans; 
int n ; 
vector<ll>g; 
void calc(ll K) {
	ll det = 0, fin = 0;  
	for(int i = 1; i <= n ; ++ i) {
		b[i] = a[i] % K;
		(det += b[i]) %= K;    
		// printf("%lld %lld\n", b[i], det); 
		fin += min(det, K - det); 
	}
	ans = min(ans, fin); 
}
int main() {
	// setIO("input"); 
	scanf("%d",&n); 
	for(int i = 1; i <= n ; ++ i) {
		scanf("%lld", &a[i]); 
		sum += a[i]; 
	}
	ans = 1005193768297041258ll; 
	for(ll i = 2; i * i <= sum; ++ i) {
		if(sum % i == 0) {
			g.pb(i);  
			while(sum % i == 0) sum /= i;  
		}
	}
	if(sum > 1) g.pb(sum); 
	for(int i = 0; i < g.size() ; ++ i) { 
		calc(g[i]); 
	}
	printf("%lld", ans == 1005193768297041258ll ? -1 : ans); 
	return 0; 
}

  

Bitwise Queries (Hard Version)

来源:CF1451E2, 2300  

交互题.  

显然可以用 $\mathrm{n-1}$ 次操作来求出每个数与第一个数字的异或.  

然后这道题有一个性质非常有用,就是数字范围在 $\mathrm{[0, n-1]}$ 之间.  

利用这个性质,发现要么所有数字都连续,要么会出现相等的情况.  

然后相等的情况就把等的找出来然后判断异或对应的 0 和 1 对应真实的值是什么.  

如果出现连续的情况就找到连续的 $\mathrm{x,x+1}$ 然后可以判断除了 0 位以外的所有.  

判断 0 位就让其中 0 位为 0 的和 1 去与一下,看看是什么.  

第一种操作为 $\mathrm{n}$, 第二种为 $\mathrm{n+1}$, 故符合答案要求.  

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N  500009 
#define pb push_back 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
vector<int>g[N], h[N];  
int val[N]; 
int n , a[N], Lg[N], mx, vis[N], pos[N];   
int AND(int x, int y) { 
	printf("AND %d %d\n", x, y); 
	printf("\n");  
	fflush(stdout);  
	int p; 
	scanf("%d", &p); 
	return p;  
} 
int XOR(int x, int y) {
	printf("XOR %d %d\n", x, y); 
	printf("\n"); 
	fflush(stdout);  
	int p; 
	scanf("%d", &p);  
	return p;  
}
void init() {
	// Lg[1] = 0; 
	for(int i = 0; i <= 17; ++ i) {
		Lg[1 << i] = i;  
	}
}
// val[j]: 第 j 位为 1 时对应的结果.  
void s1() {
	// 这个是存在相同的情况.      
	for(int i = 0; i < N ; ++ i) { 
		if(vis[i] > 1) {     
			// 个数要大于 1.  
			int x = g[i][0], y = g[i][1];  
			int c = AND(x, y);  
			for(int j = 0; j <= mx; ++ j) {
				int v = 0; 
				if(c & (1 << j)) v = 1;      
				val[j] = ((a[x] >> j) & 1) ? v : (1 ^ v);       
			}
			break;  
		}
	}
}
void s2() {   
	for(int i = 1; i <= n ; ++ i) {
		int p = (a[i] & 1) ? a[i] - 1: a[i];          
		h[p].pb(i);  
	}
	for(int i = 0; i < N ; ++ i) {
		if(h[i].size() > 1) {
			// 有两个相邻的.  
			int x = h[i][0], y = h[i][1];   
			if(x == 1 || y == 1) continue;   
			int c = AND(x, y);        
			for(int j = 1; j <= mx ; ++ j) {
				// 2 ^ i 位的情况.      
				int v = 0; 
				if(c & (1 << j)) v = 1;    
				val[j] = ((a[x] >> j) & 1) ? v : (1 ^ v);  
			}  
			// 看看 val[0] 行不行.
			if(a[h[i][0]] & 1) {
				// a[h[i][1]] 为 0.  
				int det = (AND(1, h[i][1]) & 1);   
				val[0] = (det ^ 1);  
			}
			else {
				int det = (AND(1, h[i][0]) & 1);  
				val[0] = (det ^ 1);  
			}
			break; 
		}
	}
}
int main() {
	// setIO("input");         
	scanf("%d",&n);      
	init();   
	++ vis[0]; 
	g[0].pb(1);  
	for(int i = 2; i <= n ; ++ i) {
		a[i] = XOR(1, i);      
		vis[a[i]] ++ ; 
		g[a[i]].pb(i); 
	}  
	int fl = 0; 
	for(int i = 0; i < N ; ++ i) {
		if(vis[i] > 1) {
			fl = 1; 
			break;  
		}
	}   
	mx = Lg[n] - 1;   
	if(fl) {
		s1(); 
	}
	else {
		s2();
	}
	printf("! ");  
	for(int i = 1; i <= n ; ++ i) {
		int o = 0; 
		for(int j = 0; j <= mx ; ++ j) {   
			int cur = 0; 
			if(a[i] & (1 << j)) {
				cur = 1; 
			}
			if(cur) o += val[j] << j; 
			else o += (val[j] ^ 1) << j;  
		}
		printf("%d ", o);  
	}
	fflush(stdout);  
	return 0; 
}

  

The LCMs Must be Large

来源:CF1166E, 2100 

看到题面后就知道这道题肯定有什么结论了.  

然后这道题的结论就是若所给的 $\mathrm{m}$ 个集合存在两个集合无交集就不合法.  

这个是显而易见的,然后若不出现上面的情况就一定合法,证明过程还没研究.  

#include <cstdio>
#include <bitset>
#include <cstring>
#include <algorithm>
#define N 10009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int m, n;
bitset<N>bi[51];  
int main() {
	// setIO("input");
	scanf("%d%d", &m, &n); 
	for(int i = 1; i <= m ; ++ i) {  
		int s, x; 
		scanf("%d", &s); 
		for(int j = 1; j <= s; ++ j) {
			scanf("%d", &x);  
			bi[i].set(x);   
		} 
		for(int j = 1; j < i ; ++ j) {
			if(!(bi[i] & bi[j]).count()) {
				printf("impossible"); 
				return 0; 
			}
		}   
	}
	printf("possible");  
	return 0; 
}

  

Tree Constructing

来源:CF1003E, 2100 

这道题的构造思路就是把最长链拿出来,然后其它点往上挂. 

可以预处理每个点能挂的最大深度,能挂的最大点数,然后 $\mathrm{dfs}$ 输出即可. 

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 400009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int n, d, K, cnt; 
ll f[N];   
void dfs(int cur, int ff, int de, int mx) { 
	if(cur <= n) printf("%d %d\n", cur, ff);           
	if(cnt == n) return ; 
	if(de == mx) return ; 
	for(int i = 1; i < K ; ++ i) {
		if(cnt >= n) exit(0); 
		dfs(++ cnt, cur, de + 1, mx);   
	}
}
int main() {
	// setIO("input");
	scanf("%d%d%d", &n, &d, &K);   
	f[1] = 1, ++ d; 
	if(d > n) {
		printf("NO"); 
		return 0; 
	}
	if(K == 1) {
		if(n == 1) {
			if(d == 0) printf("YES\n"); 
			else printf("NO\n"); 
		}
		else if(n == 2) {
			if(d == 2) { printf("YES\n"); printf("%d %d\n", 1, 2); }
			else printf("NO\n"); 
		} 
		else {
			printf("NO\n"); 
		}
		return 0; 
	}
	ll prev = 1;
	for(int i = 2; i < N ; ++ i) {   
		f[i] = f[i - 1] + 1ll * prev * (K - 1);    
		if(f[i] > 1ll * n) {
			for(int j = i; j < N ; ++ j) f[j] = n + 1;   
			break; 
		}
		prev = 1ll * prev * (K - 1);   
	}  
	int rem = n - d, lst = 0;    
	for(int i = 2; i < d; ++ i) {
		int dep = min(i - 1, d - i);  
		if(1ll * f[dep] * (K - 2) >= 1ll * rem) {     
			lst = i, rem = 0; 
			break; 
		}
		else {
			rem -= f[dep] * (K - 2);    
		}
	}
	if(rem) {
		printf("NO"); 
	}
	else {
		printf("YES\n");
		for(int i = 2; i <= d; ++ i) {
			printf("%d %d\n", i - 1, i);  
		}
		cnt = d; 
		for(int i = 2; i < lst; ++ i) {
			int dep = min(i - 1, d - i);  
			// 这里都是满的.  
			for(int j = 1; j <= K - 2; ++ j) {
				dfs(++ cnt, i, 1, dep);        
			}
		}
		rem = n - cnt; 
		// 最后构造 lst 即可.  
		for(int j = 1; j <= K - 2; ++ j) {   
			int dep = min(lst - 1, d - lst);
			if(rem == 0) break; 
			if(f[dep] <= rem) {
				dfs(++ cnt, lst, 1, dep);    
				rem -= f[dep];   
			}
			else {
				// f[dep] > rem. 
				dfs(++ cnt, lst, 1, dep);   
			}
		}
	}
	return 0; 
}

  

Minimax

来源:CF1530E, 2100 

构造题.   

对于要求答案最小化的构造题,先考虑答案可能的取值情况.

1. 所有字符相等,直接输出

2. 存在一个字符使得该字符出现一次,答案为 0.

3. 其余情况答案为 1.  

前 2 种情况好处理,主要看第三种. 

第三种情况的话枚举一下开头,即 aa 或 ab.  

#include <cstdio>
#include <vector>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#define N  100009 
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
char str[N]; 
int n, c[30], num[N]; 
vector<int>g;   
void print() {
	for(int i = 0; i < 26; ++ i) {
		for(int j = 1; j <= c[i]; ++ j) printf("%c", i + 'a');   
	}
	printf("\n"); 
}
int check(int x, int y) {
	// 前两个字符是一样的.
	if(c[g[x]] < 2) {
		return 0; 
	}
	int rem = c[g[x]] - 2;   
	if(n - c[g[x]] >= rem) return 1;  
	else return 0; 
}
void sol(int x, int y) {
	if(x == y) {
		// 全为第一个.   
		int cnt = 0; 
		for(int i = 1; i < g.size() ; ++ i) {
			for(int j = 1; j <= c[g[i]]; ++ j) num[++ cnt] = g[i];   
		}
		printf("%c%c", g[0] + 'a', g[0] + 'a');
		c[g[0]] -= 2;  
		int d = 1;   
		for(int i = 3; i <= n ; ++ i) {
			if((i & 1)) {
				printf("%c", 'a' + num[d]), ++ d;  
			}
			else {
				if(c[g[0]]) printf("%c", 'a' + g[0]), -- c[g[0]]; 
				else printf("%c", 'a' + num[d]) , ++ d;  
			}
		}
		printf("\n"); 
	}
	else {
		// x != y, abbbbbbbbbbbbbaaaaa
		//         abaaaaaaaaaaaaaaaaaaaaacbbbbbbbbbbb
		if(g.size() == 2) {
			printf("%c%c", g[0] + 'a', g[1] + 'a');
			c[g[0]] -= 1, c[g[1]] -= 1;  
			for(int i = 1; i <= c[g[1]]; ++ i) printf("%c", 'a' + g[1]);   
			for(int i = 1; i <= c[g[0]]; ++ i) printf("%c", 'a' + g[0]);  
		}
		else {
			printf("%c%c", g[0] + 'a', g[1] + 'a'); 
			c[g[0]] -= 1, c[g[1]] -= 1; 
			for(int i = 1; i <= c[g[0]]; ++ i) printf("%c", 'a' + g[0]); 
			printf("%c", g[2]+ 'a'), --c[g[2]];    
			for(int i = 1; i < g.size() ; ++ i) {
				for(int j = 1; j <= c[g[i]]; ++ j) printf("%c", 'a' + g[i]);  
			}
		}
		printf("\n"); 
	}
}
void cal2() {
	// in this case, length of border = 1.    
	if(check(0, 0)) sol(0, 0); 
	else sol(0, 1);       
}
// border = 1 的情况.
void pt() {
	int mn = 0; 
	for(int i = 0; i < 26; ++ i) if(c[i] == 1) { mn = i; break; }  
	printf("%c", mn + 'a'); 
	for(int i = 0; i < 26; ++ i) if(i != mn) {
		for(int j = 1; j <= c[i] ; ++ j) printf("%c", i + 'a');  
	}		
	printf("\n"); 
}
void solve() {
	scanf("%s", str + 1); 
	n = strlen(str + 1);   
	for(int i = 1; i <= n; ++ i) {
		int cur = str[i] - 'a'; 
		if(!c[cur]) g.pb(cur);  
		++c[cur];  
	}
	sort(g.begin(), g.end());
	if(g.size() == 1 || g.size() == n) 
		print();   
	else {
		// 还有 border = 0 的情况:
		int mn = -1;
		for(int i = 0; i < 26; ++ i) if(c[i] == 1) {mn = i; break; } 
		if(mn != -1) 
			pt();   
		else cal2();  
	}
	memset(c, 0, sizeof(c)), g.clear(); 
}
int main() {
	// setIO("input");
	int T; 
	scanf("%d", &T); 
	while(T--) solve();
	return 0;
}

  

Permutation Shift

来源:CF1553E, 2100 

给定一个排列,可以随意交换两个数,问最少多少次变成 1 到 n.  

这个问题可以将数字和位置连边,然后判断有几个环,答案就是 n - 环数. 

这道题直观上肯定先去进行枚举位移,然后判断是否合法. 

然后题中交换次数给了一个奇怪的限定,退一下式子发现答案要满足正确位置大于等于 n/3.  

而又有所有正确位置之和为 n, 故最多只有 3 种 k 值. 

最后暴力枚举,然后连边跑 dfs 判断即可. 

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define N  300009 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
vector<int>G[N], an; 
int n, m, p[N], vis[N], cnt[N], a[N];  
void dfs(int x) {
	vis[x] = 1; 
	for(int i = 0; i < G[x].size() ; ++ i) {
		if(!vis[G[x][i]]) dfs(G[x][i]); 
	}
}
void solve() {
	scanf("%d%d", &n, &m);  
	for(int i = 0; i <= n ; ++ i) cnt[i] = 0; 
	for(int i = 1; i <= n ; ++ i) {
		scanf("%d", &p[i]); 
		if(p[i] >= i) cnt[p[i] - i] ++ ;   
		else {
			cnt[n - i + p[i]] ++ ;   
		} 
	}    
	an.clear();  
	for(int i = 0; i < n ; ++ i) {

		if(cnt[i] < n - 2 * m) continue;
		// printf("%d %d\n", i, cnt[i]); 
		for(int j = 1; j <= n ; ++ j) {
			int nex = ((j - 1) + i) % n + 1;  
			a[nex] = p[j];    
		}
		for(int j = 1; j <= n ; ++ j) {
			if(a[j] != j) G[j].pb(a[j]);    
		}
		int c = 0; 
		for(int j = 1; j <= n ; ++ j) {
			if(!vis[j]) ++ c, dfs(j);     
		}
		// printf("%d\n", c); 
		for(int j = 1; j <= n ; ++ j) G[j].clear(), vis[j] = 0;   
		if(n - c <= m) an.pb((n - i) % n);   
	}
	printf("%d ", an.size()); 
	sort(an.begin(), an.end()); 
	for(int i = 0; i < an.size() ; ++ i) printf("%d ", an[i]); 
	printf("\n");   
}
int main() {
	// setIO("input"); 
	int T; 
	scanf("%d", &T); 
	while(T-- ) solve(); 
	return 0; 
}

  

Errich-Tac-Toe (Hard Version)

来源:CF1450C2, 2300

构造题. 

在简单版本中,可以直接按 3 的余数进行棋盘染色,然后每次只改变一类点. 

在本题中,可以强制令两类点都改成 $X$ 与 $O$.  

利用抽屉原理,有 3 种染色方案且和为 $\mathrm{K}$, 所以最小的方案肯定小于 K/3.  

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N  305
#define ll long long 
#define pb push_back 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int n ; 
char str[N][N]; 
void print(int x, int y, int z) {
	for(int i = 1; i <= n ; ++ i) {
		for(int j = 1; j <= n ; ++ j) {
			if(str[i][j] == '.') {
				printf("%c", str[i][j]); 
				continue;  
			}
			int d = (i + j) % 3; 
			if(d == 0) {
				if(x == -1) printf("%c", str[i][j]); 
				else if(x == 0) printf("O"); 
				else printf("X"); 
			}
			if(d == 1) {
				if(y == -1) printf("%c", str[i][j]); 
				else if(y == 0) printf("O"); 
				else printf("X"); 
			}
			if(d == 2) {
				if(z == -1) printf("%c", str[i][j]); 
				else if(z == 0) printf("O"); 
				else printf("X"); 
			}
		}
		printf("\n");  
	}
}
void solve() { 
	scanf("%d", &n); 
	for(int i = 1; i <= n ; ++ i) {
		scanf("%s", str[i] + 1); 
	} 
	int c[4][2], tot = 0; 
	memset(c, 0, sizeof(c));  
	for(int i = 1; i <= n ; ++ i) {
		for(int j = 1; j <= n ; ++ j) {
			int d = (i + j) % 3;   
			if(str[i][j] == '.') continue;
			++ tot;      
			if(str[i][j] == 'X') c[d][0] ++ ; 
			else c[d][1] ++ ;  
		}
	}
	if(c[0][0] + c[1][1] <= tot / 3) print(0, 1, -1);   
	else if(c[1][0] + c[2][1] <= tot / 3) print(-1, 0, 1); 
	else print(1, -1, 0);   
}
int main() {
	// setIO("input"); 
	int T; 
	scanf("%d", &T); 
	while(T--) solve(); 
	return 0; 
}

  

Trains and Statistic

来源:CF675E, 2300  

贪心题.  

令 $\mathrm{sum[i]}$ 表示以 $\mathrm{i}$ 为起点的答案和.  

那么显然只需跳一步的就是 $\mathrm{[i, a[i]]}$  内的.  

然后如果是在 $\mathrm{a[i]}$ 之外,肯定要找区间内跳的最远的.  

然后直接转移一下即可.  

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N  200009 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int a[N], n, Lg[N], mi[N][20];    
ll sum[N]; 
void init() {
	Lg[1] = 0; 
	for(int i = 2; i < N ; ++ i) Lg[i] = Lg[i >> 1] + 1;   
	for(int i = 1; i <= n ; ++ i) mi[i][0] = i;  
	for(int i = 1; i < 19 ; ++ i) {
		for(int j = 1; j + (1 << i) - 1 <= n ; ++ j) {
			if(a[mi[j][i - 1]] > a[mi[j + (1 << (i - 1))][i - 1]])         
				mi[j][i] = mi[j][i - 1]; 
			else mi[j][i] = mi[j + (1 << (i - 1))][i - 1];  
		}
	}
}
int query(int l, int r) {
	int p = Lg[r - l + 1]; 
	if(a[mi[l][p]] > a[mi[r - (1 << p) + 1][p]]) 
		return mi[l][p]; 
	else return mi[r - (1 << p) + 1][p];  
}
int main() {
	// setIO("input");      
	scanf("%d",&n); 
	for(int i = 1; i < n ; ++ i) {
		scanf("%d", &a[i]); 
	}
	init(); 
	sum[n] = 0;      
	for(int i = n - 1; i >= 1; -- i) {
		int l = i + 1, r = a[i];   
		if(a[i] == n) {
			sum[i] = n - i;  
		}
		else {
			int id = query(l, r);       
			sum[i] = sum[id] + id + n - i - a[i];  
		}
		// printf("%d %lld\n", i, sum[i]);  
	}
	ll ans = 0; 
	for(int i = 1; i <= n ; ++ i) ans += sum[i]; 
	printf("%lld", ans); 
	return 0; 
}

  

Magic Stones

来源:CF1110E, 2200   

需要手玩性质.  

显然,合法的必要条件是首末两个数字必须对应相等.  

然后我们需要寻求变换过程中的不变量,看看两个序列是否有给定量可以判定相等.  

然后发现变换之后差分数组等于说是左右交换,所以只需判断一下两个序列差分数组是否排完序后相等即可.   

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define N  1000009 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int n, c[N], t[N]; 
void solve() {
	scanf("%d", &n); 
	for(int i = 1; i <= n ; ++ i) {
		scanf("%d", &c[i]); 
	}
	for(int i = 1; i <= n ; ++ i) {
		scanf("%d", &t[i]); 
	}
	if(c[1] != t[1] || c[n] != t[n]) {
		printf("No\n"); 
	}
	else {
		for(int i = n; i >= 2; -- i) {
			c[i] -= c[i - 1]; 
			t[i] -= t[i - 1]; 
		}
		sort(c + 2, c + 1 + n); 
		sort(t + 2, t + 1 + n); 
		for(int i = 2; i <= n ; ++ i) {
			if(c[i] != t[i]) {
				printf("No\n"); 
				return ; 
			}
		}
		printf("Yes\n"); 
	}
}
int main() {
	// setIO("input"); 
	int T = 1;  
	// scanf("%d", &T); 
	while(T -- ) solve(); 
	return 0; 
}

  

Placing Rooks

来源:CF1342E, 2300  

可以将问题转化为:在区间 $\mathrm{[1, n-k]}$ 中选择 $\mathrm{n}$ 个数且 $1$ 至 $\mathrm{n-k}$ 都被选到.  

然后利用容斥原理,枚举交集为 $\mathrm{i}$ 个数空着的情况,最后加和一下即可.   

注意特判无解和 $\mathrm{k=0}$ 的情况.  

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N  200009 
#define ll long long
#define mod 998244353 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
int n, k;   
ll K;
int fac[N], inv[N]; 
int qpow(int x, int y) {
    int tmp = 1; 
    for(; y; y >>= 1, x = (ll)x * x % mod) 
        if(y & 1) tmp = (ll)tmp * x % mod; 
    return tmp; 
} 
int get_inv(int x) {
    return qpow(x, mod - 2); 
}
void init() {
    fac[0] = inv[0] = 1; 
    for(int i = 1; i < N ; ++ i) {
        fac[i] = (ll)fac[i - 1] * i % mod; 
    }
    inv[N - 1] = get_inv(fac[N - 1]);  
    for(int i = N - 2; i >= 1; -- i) {
        inv[i] = (ll)inv[i + 1] * (i + 1) % mod;   
    }
}
int C(int x, int y) {
    return (ll)fac[x] * inv[y] % mod * inv[x - y] % mod;  
}
// h 个空位.  
int F(int h) {
    return (ll)C(n - k, h) * qpow(n - k - h, n) % mod;   
}
void solve() {   
    k = K;        
    int ans = 0; 
    for(int i = 0; i <= n - k; ++ i) {
        int d = (i & 1) ? (mod - 1) : 1;  
        (ans += (ll)C(n, k) * d % mod * F(i) % mod) %= mod;   
    }
    // printf("%d\n", ans); 
    printf("%d", (ll)((ll)ans * 2 % mod) % mod);      
}
int main() {
    // setIO("input");     
    init();     
    scanf("%d%lld", &n, &K);  
    if(K == 0) {
        printf("%d", fac[n]);    
    }
    else if(K > n) printf("0"); 
    else {
        solve();  
    }
    return 0; 
}

  

MEX Sequences

来源:CF1073E   

构造一下,发现合法的情况有 3 种:

1. 全为 1

2. 一个逐次 +1 的序列.

3. 一个前半部分逐次加一,后半部分相差为 2 的波动序列.  

针对 3 种情况 $\mathrm{DP}$ 弄一下即可.  

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N  500009 
#define ll long long
#define mod 998244353 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int qpow(int x, int y) {
    int tmp = 1; 
    for(; y; y >>= 1, x = (ll)x * x % mod) {
        if(y & 1) tmp = (ll)tmp * x % mod; 
    }
    return tmp;  
}
int a[N], pw[N], f[N], cnt[N], g[N][2], suf[N][2], n ; 
void init() {
    pw[0] = 1; 
    for(int i = 1; i < N ; ++ i) {
        pw[i] = (ll)pw[i - 1] * 2 % mod;  
    }   
}
void solve() {
    scanf("%d", &n);    
    int c1 = 0; 
    for(int i = 1; i <= n ; ++ i) {
        scanf("%d", &a[i]); 
        if(a[i] == 1) ++ c1 ;  
    }       
    // g[i][0]: i and i - 2 
    // g[i][1]: i and i + 2  
    for(int i = n ; i >= 1; -- i) {
        // calc g[a[i]][0];  
        int c0 = (ll)(suf[a[i]][0] + 1) % mod;   
        if(a[i] - 2 >= 0) (c0 += suf[a[i] - 2][1]) %= mod;     
        int c1 = (ll)(suf[a[i]][1] + 1) % mod;   
        if(a[i] + 2 <= n) (c1 += suf[a[i] + 2][0]) %= mod;       
        g[i][0] = c0, g[i][1] = c1;   
        (suf[a[i]][0] += c0) %= mod;  
        (suf[a[i]][1] += c1) %= mod;   
    }         
    int ans = 0; 
    for(int i = 1; i <= n ; ++ i) {     
        if(a[i] == 0) {    
            ++ cnt[0];  
            f[0] = (ll)(pw[cnt[0]] - 1 + mod) % mod;  
        }
        else {
            ++ cnt[a[i]];  
            f[a[i]] = (ll)f[a[i]] * 2 % mod;    
            (f[a[i]] += f[a[i] - 1]) %= mod;   
        }    
        if(a[i] - 2 >= 0) {
            (ans += (ll)f[a[i] - 2] * g[i][0] % mod) %= mod;  
        }
    }
    (ans += (ll)(pw[c1] - 1 + mod) % mod) %= mod;  
    for(int i = 0; i <= n ; ++ i) {
        (ans += f[i]) %= mod;  
    }
    for(int i = 0; i <= n ; ++ i) {
        f[i] = g[i][0] = g[i][1] = cnt[i] = suf[i][0] = suf[i][1] = 0; 
    }
    printf("%d\n", ans); 
} 
int main() {
    // setIO("input");   
    int T; 
    init();  
    scanf("%d", &T); 
    while(T--) solve(); 
    return 0; 
}

  

Segment Sum

来源:CF1073E, 2300  

数位 DP.  

可以二进制将每种数字选不选压缩起来,然后跑两遍数位 DP. 

第一遍记录方案数,第二遍统计答案. 

这里要注意要把第一遍所维护的 $\mathrm{f}$ 数组所有情况都要枚举到,因为第二种情况要用.  

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N  20 
#define ll long long 
#define mod 998244353 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int lowbit(int x) {
    return x & (-x); 
}
int cnt[1 << 12], a[N], K,  len, f[N][1 << 12][2][2], g[N][1 << 12][2][2], pw[N];         
// DP 状态: f[cur][sta][is][fl];      
void init() {  
    cnt[0] = 0;          
    for(int i = 1; i < (1 << 12) ; ++ i) cnt[i] = cnt[i - lowbit(i)] + 1; 
    pw[0] = 1;        
    for(int i = 1; i <= 18 ; ++ i) pw[i] = (ll)pw[i - 1] * 10 % mod;  
}   
int dfs(int cur, int sta, int is, int fl) {     
    if(cur > len) {            
        return f[cur][sta][is][fl] = (!is);   
    }   
    if(f[cur][sta][is][fl] != -1) return f[cur][sta][is][fl];      
    int d = fl ? a[cur] : 9;  
    int an = 0;   
    // printf("%d %d\n", cur, d); 
    for(int i = 0; i <= d; ++ i) {
        if(is) {
            // 前面全是 0.    
            if(i == 0) {
                an += dfs(cur + 1, sta, is, fl && (i == d));    
                if(an >= mod) an -= mod;  
            }
            else {
                if(cnt[sta | (1 << i)] > K) 
                    continue;   
                an += dfs(cur + 1, sta | (1 << i), 0, fl && (i == d));   
                if(an >= mod) an -= mod;  
            }
        }
        else { 
            if(cnt[sta | (1 << i)] > K) continue;  
            an += dfs(cur + 1, sta | (1 << i), 0, fl && (i == d));   
            if(an >= mod) an -= mod; 
        }
    }   
    return f[cur][sta][is][fl] = an;  
}
int cal(int cur, int sta, int is, int fl) {   
    //printf("%d\n", sta); 
    if(cur > len) {
        return 0; 
    }
    if(g[cur][sta][is][fl] != -1) 
        return g[cur][sta][is][fl];      
    int d = fl ? a[cur] : 9;  
    int an = 0;   
    g[cur][sta][is][fl] = 0; 
    for(int i = 0; i <= d; ++ i) {
        if(is) {
            // 前面全是 0.    
            if(i == 0) {
                an += cal(cur + 1, sta, is, fl && (i == d));    
                if(an >= mod) an -= mod;  
            }
            else {
                if(cnt[sta | (1 << i)] > K) 
                    continue;   
                an += (ll)(cal(cur + 1, sta | (1 << i), 0, fl && (i == d)) + (ll)i * pw[len - cur] % mod * f[cur + 1][sta | (1 << i)][0][fl && (i == d)] % mod) % mod;     
                an %= mod;      
            }
        }
        else {
            if(cnt[sta | (1 << i)] > K) continue;  
            an += (ll)((ll)pw[len - cur] * i % mod * f[cur + 1][sta | (1 << i)][0][fl && (i == d)] % mod + cal(cur + 1, sta | (1 << i), 0, fl && (i == d))) % mod; 
            an %= mod;     
        }
    }       
    return g[cur][sta][is][fl] = an;  
}
int solve(ll X) {
    memset(f, -1, sizeof(f));  
    memset(g, -1, sizeof(g));         
    len = 0;   
    while(X) {
        a[ ++ len ] = X % 10;     
        X /= 10; 
    }     
    for(int i = 1; i <= len / 2; ++ i) swap(a[i], a[len - i + 1]);   
    dfs(1, 0, 1, 1);  
    return cal(1, 0, 1, 1);   
}

int main() {
    // setIO("input");  
    init();  
    ll L, R; 
    scanf("%lld%lld%d", &L, &R, &K);    
    printf("%d\n", (ll)(solve(R) - solve(L - 1) + mod) % mod); 
    return 0; 
}

  

Mike and Frog

来源:CF547A, 2200

这道题没有思维难度,但是有很多情况需要特殊讨论一下.  

一个数字的步数表述成 $\mathrm{y=kx+b}$ 的形式.  

$\mathrm{b}$ 是从初始位置走到该数字的步数,$\mathrm{k}$ 是模意义下的循环节.

这里要分几种情况讨论:

1. 两个数都具有 $\mathrm{k,b}$. 

2. 两个数都没有 $\mathrm{k}$.  

3. 其中一个数字有 $\mathrm{k}$.  

第一种情况用 $\mathrm{exgcd}$ 求解,后面算一下即可. 

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define pb push_back 
#define ll long long 
#define N  1000009 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
struct Math {
    int exgcd(ll a, ll b, ll &x, ll &y) {
        if(!b) {
            x = 1, y = 0; 
            return a; 
        }
        int gc = exgcd(b, a % b, x, y);  
        ll t = x;
        x = y, y = t - (a / b) * y; 
        return gc;   
    }
    ll solve(int a, int b, int c) {
        ll x, y; 
        int c0 = exgcd(a, b, x, y);   
        if(c % c0) return -1; 
        ll det = abs(b / c0);               
        ll an = (x * (c / c0) % det + det) % det;       
        ll y0 = (c - an * a) / b;   
        if(y0 >= 0) return an; 
        else {
            // y0 < 0  
            ll d = abs(a / c0); 
            ll t = (abs(y0) + d - 1) / d;      
            return an + 1ll * t * det;     
        }           
    }
}T; 
int m; 
int h[2], x[2], y[2], a[2], vis[N]; 
struct node {
    int k, b; 
    node(int k = 0, int b = 0):k(k), b(b){}  
}A[2]; 
node solve(int id) {
    memset(vis, -1, sizeof(vis));   
    int cur = h[id];  
    vis[cur] = 0;  
    while(1) {
        int nex = (ll)(1ll * x[id] * cur % m + y[id]) % m;       
        if(vis[nex] != -1) break; 
        vis[nex] = vis[cur] + 1, cur = nex;        
    }    
    if(vis[a[id]] == -1) {
        return node(-1, -1); 
    }
    else {
        int b = vis[a[id]], k = 0;   
        // b 为步数.
        cur = a[id]; 
        memset(vis, -1, sizeof(vis));             
        while(1) {
            ++ k;
            int nex = (ll)((ll)cur * x[id] % m + y[id]) % m;              
            if(nex == a[id]) {
                return node(k, b);  
            }
            if(vis[nex] != -1) break;  
            vis[nex] = 1, cur = nex;  
        }   
        return node(-233, b);    
    }
} 
int main() {
    // setIO("input"); 
    scanf("%d",&m); 
    for(int i = 0; i < 2; ++ i) {
        scanf("%d%d%d%d",&h[i],&a[i],&x[i],&y[i]);
        A[i] = solve(i);   
        if(A[i].k == -1) {
            printf("-1"); 
            return 0; 
        }
    }  
    if(A[0].k == -233 || A[1].k == -233) {
        if(A[0].k == -233 && A[1].k == -233) {
            printf("%d", A[0].b == A[1].b ? A[0].b: -1); 
            return 0;
        }  
        else if(A[0].k == -233) {
            // x * A[1].k + A[1].b == A[0].b;   
            if(A[0].b - A[1].b >= 0 && (A[0].b - A[1].b) % A[1].k == 0) {
                printf("%d", A[0].b); 
            }
            else printf("-1"); 
            return 0; 
        }
        else {
            if(A[1].b - A[0].b >= 0 && (A[1].b - A[0].b) % A[0].k == 0) {
                printf("%d", A[1].b); 
            }
            else printf("-1");
            return 0;  
        }
    }
    // A[0] and A[1] 
    ll fin = T.solve(A[0].k, -A[1].k, A[1].b - A[0].b);   
    if(fin == -1) printf("-1"); 
    else {
        printf("%lld", fin * A[0].k + A[0].b);  
    }
    return 0; 
}

  

Quantifier Question

来源:CF1344C, 2600

可以将大小关系建立一张图,显然这张图不可以有环. 

考虑一个点可以被设为任意的条件: 

这个点的前驱和后继不能有小于这个点的编号.   

然后每种这样的点都能被取到,于是跑一个拓扑排序记录一下入队序列更新即可.  

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#define N  200009 
#define pb push_back 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int n, m, deg[N], A[N], m1[N], m2[N], cn, cnt;  
queue<int>q; 
vector<int>G[N], e[N]; 
int main() {
    // setIO("input"); 
    scanf("%d%d", &n, &m); 
    for(int i = 1; i <= m ; ++ i) {
        int x, y; 
        scanf("%d%d", &x, &y); 
        G[x].pb(y); 
        ++ deg[y];  
        e[y].pb(x);   
    }    
    for(int i = 1; i <= n ; ++ i) {
        if(!deg[i]) {
            A[++ cnt] = i, q.push(i); 
        }
    }   
    while(!q.empty()) {
        int u = q.front(); q.pop();  
        for(int i = 0; i < G[u].size() ; ++ i) {
            int v = G[u][i];   
            -- deg[v]; 
            if(!deg[v]) {
                A[++ cnt] = v, q.push(v); 
            }
        }
    }   
    for(int i = 1; i <= n ; ++ i) {
        m1[i] = m2[i] = i; 
    }  
    if(cnt < n) {
        printf("-1"); 
    }
    else {
        for(int i = 1; i <= cnt ; ++ i) {
            int u = A[i]; 
            // 前驱,即 y -> u  
            for(int j = 0; j < e[u].size() ; ++ j) {
                int y = e[u][j];  
                m1[u] = min(m1[u], m1[y]);   
            }
        }
        for(int i = cnt; i >= 1; -- i) {
            int u = A[i]; 
            for(int j = 0; j < G[u].size() ; ++ j) {
                int y = G[u][j];  
                m2[u] = min(m2[u], m2[y]);  
            }
        }
        int sc = 0; 
        for(int i = 1; i <= n ; ++ i) {
            if(min(m1[i], m2[i]) >= i) {
                ++ sc; 
            }
        }
        printf("%d\n", sc); 
        for(int i = 1; i <= n ; ++ i) {
            if(min(m1[i], m2[i]) >= i) {
                printf("A");  
            }
            else {
                printf("E");  
            }
        }
    } 
    return 0; 
}

  

Phoenix and Earthquake

来源:CF1515F, 2600 

显然问题有解的必要条件是所有点权和大于等于 $\mathrm{(n-1)x}$.   

然后有一个结论就是这还是一个充分条件.  

证明:随便拿出 $\mathrm{n}$ 个点的生成树,并随便拿出一个叶子. 

若叶子的权值大于等于 $\mathrm{x}$, 将叶子和父亲合并后对剩余 $\mathrm{n-1}$ 个块更优. 

若叶子的权值小于 $\mathrm{x}$, 则先将父亲所在的 $\mathrm{n-1}$ 个点的块合并,最后合并叶子. 

用数学归纳法就证完了. 

然后求解的时候就按照上述方法来做,开一个队列和栈. 

权值大的边加入队列,权值小的边加入栈,然后边加边更新权值即可. 

#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <stack>
#include <algorithm>
#define N  300009 
#define ll long long
#define pb push_back  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int fa[N];  
int hd[N], to[ N << 1 ], nex[N << 1];  
int n, m, val,from[N], vis[N], edges; 
ll a[N]; 
queue<int>q; 
stack<int>S; 
void add(int u, int v) {
    nex[++ edges] = hd[u]; 
    hd[u] = edges, to[edges] = v; 
}  
vector<int>G[N];      
void init() {
    for(int i = 0; i < N ; ++ i) fa[i] = i; 
}     
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]); 
}
void merge(int x, int y) {
    x = find(x); 
    y = find(y); 
    if(x != y) fa[x] = y; 
}
void dfs(int x, int ff, int id) {
    vis[x] = 1; 
    for(int i = hd[x]; i ; i = nex[i]) {
        int v = to[i]; 
        if(vis[v]) {
            continue; 
        }   
        // printf("%d\n", v); 
        dfs(v, x, i); 
    }
    if(x == 1) {
        return ; 
    }
    else {       
        if(a[x] >= val) {
            q.push(id), a[ff] += a[x] - val;   
        }
        else {
            S.push(id);  
        }
    }
}
int main() {
    // setIO("input"); 
    scanf("%d%d%d", &n, &m, &val);
    ll sum = 0ll;  
    for(int i = 1; i <= n ; ++ i) {
        scanf("%lld", &a[i]), sum += a[i];  
    }
    edges = 1;   
    for(int i = 1; i <= m ; ++ i) {
        int x, y; 
        scanf("%d%d", &x, &y); 
        add(x, y), add(y, x); 
        merge(x, y); 
    }
    int cn = 0; 
    for(int i = 1; i <= n ; ++ i) {
        if(find(i) == i) ++ cn; 
    } 
    if(cn > 1 || sum < 1ll * (n - 1) * val) {
        printf("NO"); 
    }
    else {
        printf("YES\n");
        dfs(1, 0, 0);     
        while(!q.empty()) {
            printf("%d\n", q.front() >> 1); 
            q.pop(); 
        }
        while(!S.empty()) {
            printf("%d\n", S.top() >> 1);  
            S.pop();  
        }
    }
    return 0; 
}

  

Digit Tree

来源:CF715C, 2800

路径问题考虑用点分治进行处理.  

考虑当前的分治中心 $\mathrm{x}$, 对于两条路径 $\mathrm{d1,d2}$ 如何合并.  

令 $\mathrm{d1,d2}$ 分别表示从下到上,从上到下的路径.   

然后$\mathrm{d1}$ 和 $\mathrm{d2}$ 能合并的条件是 $\mathrm{d1=-d2 \times 10^{-dep2}}$.   

有这个式子后开两个 $\mathrm{map}$ 分别进行统计即可.  

这里注意这个 $\mathrm{m}$ 不一定是质数,所以需要用扩展欧几里得算法求逆元.  

#include <cstdio>
#include <map> 
#include <vector>
#include <cstring>
#include <algorithm>
#define N  100009 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std; 
ll ans; 
int n, mod, inv, hd[N], to[N << 1], nex[N << 1], val[N << 1], edges, sn, root;  
int f[N], size[N], vis[N], dep[N], bu[N], base[N];    
map<int, int>c1, c2;    
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if(!b) {
        x = 1, y = 0; 
        return a; 
    }
    ll gc = exgcd(b, a % b, x, y);  
    ll t = x;  
    x = y, y = t - (a / b) * y;  
    return gc; 
} 
void init() {
    ll x0, y0; 
    exgcd(10, mod, x0, y0);  
    x0 = (x0 % mod + mod) % mod;  
    inv = (int)x0;  
}
void add(int u, int v, int c) {
    nex[++edges] = hd[u]; 
    hd[u] = edges, to[edges] = v, val[edges] = c;  
}
void getroot(int x, int ff) {
    size[x] = 1, f[x] = 0; 
    for(int i = hd[x]; i ; i = nex[i]) {
        int v = to[i]; 
        if(v == ff || vis[v]) continue;  
        getroot(v, x), size[x] += size[v];  
        f[x] = max(f[x], size[v]);  
    }
    f[x] = max(f[x], sn - f[x]);    
    if(f[x] < f[root]) root = x;   
}
// 第一波.  
void calc(int x, int ff, int d1, int d2) {
    dep[x] = dep[ff] + 1;
    // 考虑 (d1, d2) 对前面的贡献.  
    ans += c2[d1];  
    ans += c1[(ll)(mod - d2) * bu[dep[x]] % mod];     
    for(int i = hd[x]; i ; i = nex[i]) {
        int v = to[i]; 
        if(v == ff || vis[v]) {
            continue;  
        }
        calc(v, x, (ll)(d1 + (ll)base[dep[x]] * val[i] % mod) % mod, (ll)((ll)d2 * 10 % mod + val[i]) % mod);        
    }   
}
// 第二波. 
void getdis(int x, int ff, int d1, int d2) {    
    dep[x] = dep[ff] + 1;        
    if(!d1) ++ ans; 
    if(!d2) ++ ans; 
    c1[d1] ++ ;  
    c2[(ll)(mod - d2) * bu[dep[x]] % mod] ++ ;   
    // c1 = d1  
    // c2 = -d2 * 10 ^ (-dep)     
    for(int i = hd[x]; i ; i = nex[i]) {
        int v = to[i]; 
        if(v == ff || vis[v]) {
            continue;  
        }
        getdis(v, x, (ll)(d1 + (ll)base[dep[x]] * val[i] % mod) % mod, (ll)((ll)d2 * 10 % mod + val[i]) % mod);    
    }   
}
void dfs(int x) {
    vis[x] = 1;  
    for(int i = hd[x]; i ; i = nex[i]) {
        int v = to[i]; 
        if(vis[v]) continue;   
        dep[x] = 0; 
        calc(v, x, val[i] % mod, val[i] % mod); 
        getdis(v, x, val[i] % mod, val[i] % mod);  
    }  
    // 当前计算完毕.  
    c1.clear(); 
    c2.clear(); 
    for(int i = hd[x]; i ; i = nex[i]) {
        int v = to[i]; 
        if(vis[v]) continue;  
        root = 0, sn = size[v], getroot(v, 0);  
        dfs(root);  
    }
}
int main() {
    // setIO("input");  
    scanf("%d%d", &n, &mod);  
    init(); 
    for(int i = 1; i < n ; ++ i) {
        int x, y, z; 
        scanf("%d%d%d", &x, &y, &z);  
        ++ x, ++ y;  
        add(x, y, z), add(y, x, z); 
    }
    bu[0] = 1, base[0] = 1; 
    for(int i = 1; i < N ; ++ i) {
        bu[i] = (ll)bu[i - 1] * inv % mod;  
        base[i] = (ll)base[i - 1] * 10 % mod;  
    }
    f[root = 0] = N, sn = n, getroot(1, 0);      
    dfs(root); 
    printf("%lld", ans); 
    return 0; 
}

  

 

posted @ 2021-11-23 18:41  guangheli  阅读(29)  评论(0)    收藏  举报