https://codeforces.com/contest/1363 
C. Game On Leaves 
 
题意:给定一棵树和一个目标节点,两个人轮流操作,每次选一个叶子拿掉,先拿掉目标节点的赢。
以目标节点为根,拿完只剩一条链的肯定输了,所以都是拿长链或者拿完还有边的节点。最后剩下目标节点和另一个节点。拿掉的个数只与结点数有关。所以判断节点数奇偶。
一直读错题了,以为拿完一条链就能拿目标节点了,实际是要变成叶子才能拿,搞成了尼姆游戏。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <bits/stdc++.h>  using  namespace  std;#define  ll long long const  int  INF = 0x3f3f3f3f ;const  ll inf = 0x3f3f3f3f3f3f3f3f ;const  int  N = 2e5  + 10 ;int  T;int  main ()  	scanf ("%d" , &T); 	while  (T--) { 		int  n, x, d = 0 ; 		scanf ("%d%d" , &n, &x); 		for  (int  i = 1 ; i < n; i++) { 			int  u, v; 			scanf ("%d%d" , &u, &v); 			if  (u == x || v == x)d++; 		} 		if  (d <= 1 )puts ("Ayush" ); 		else  { 			if  (n & 1 )puts ("Ashish" ); 			else  puts ("Ayush" ); 		} 	} 	return  0 ; } 
 
D. Guess The Maximums 
 
题意:交互题,有一个长度n的数组,给定k个不相交的下标集,对每个集合猜出不在集合里的最大的数。每次询问一个任意的自己构造的下标集,可以得到集合里的数的最大值。询问次数最大为log。
看到询问次数应该就能猜出二分。
由于这k个集合不相交,所以数组中最大的数必定最多在一个集合里,那么对于其他的集合,不在集合里的最大的数就是数组的最大值。而对于包含最大值的那个集合,直接询问它的结果。
所以只要求出数组的最大值即可。
就类似线段树那样二分,先问整个集合,得到最大值大小,再问左区间,如果左区间最大值等于整个区间最大值,就说明在左区间里,否则右区间。保证了询问次数卡在一个log。
如果数组有两个最大值也是一样的。只要找到一个最大值就好了。
注意清空,还有大写的Correct!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <bits/stdc++.h>  using  namespace  std;#define  ll long long const  int  INF = 0x3f3f3f3f ;const  ll inf = 0x3f3f3f3f3f3f3f3f ;const  int  N = 2e5  + 10 ;int  T;vector<int >vc[N]; int  ans[N], nt[1010 ];string s; int  main ()  	cin >> T; 	while  (T--) { 		int  n, k; 		cin >> n >> k; 		for  (int  i = 1 ; i <= k; i++) { 			vc[i].clear (); 			int  m, x; 			cin >> m; 			for  (int  j = 0 ; j < m; j++) { 				cin >> x; 				vc[i].push_back (x); 			} 		} 		int  mx; 		cout << "? "  << n; 		for  (int  i = 1 ; i <= n; i++)cout << ' '  << i; 		cout << endl; 		cin >> mx; 		int  L = 1 , R = n, tmp; 		while  (L < R) { 			int  mid = (L + R) / 2 ; 			cout << "? "  << mid - L + 1 ; 			for  (int  i = L; i <= mid; i++)cout << ' '  << i; 			cout << endl; 			cin >> tmp; 			if  (tmp == mx)R = mid; 			else  L = mid + 1 ; 		} 		for  (int  i = 1 ; i <= k; i++) { 			bool  ys = 0 ; 			for  (int  j : vc[i]) { 				if  (j == L) { ys = 1 ; break ; } 			} 			if  (ys) { 				cout << "? "  << n - (int )vc[i].size (); 				memset (nt, 0 , sizeof  				for  (int  j : vc[i])nt[j] = 1 ; 				for  (int  j = 1 ; j <= n; j++)if  (!nt[j])cout << ' '  << j; 				cout << endl; 				cin >> ans[i]; 			} 			else  { 				ans[i] = mx; 			} 		} 		cout << "!" ; 		for  (int  i = 1 ; i <= k; i++)cout << ' '  << ans[i]; 		cout << endl; 		cin >> s; 		if  (s[0 ] != 'C' )return  0 ; 	} 	return  0 ; } 
 
E. Tree Shuffling 
 
题意:给定一棵树,每个节点为0或1,且每个节点有权值,每次操作选一个节点u,从它的子树里选一些节点重排01,花费为选的结点数*u的权值,要求最终每个点的01值都为规定的值。求最小花费。
贪心 
选择一个点u,再选一些子树里的点,则选的每个点的花费都是u的权值。所以只要贪心地选择最便宜的节点,把它子树里所有不符合规定的节点都处理完。
但是u的子树处理完后可能会有剩下的没有配对的节点,这些节点只能留给它的祖先们处理了,所以要通知祖先们u的子树的变化。但也不能直接遍历通知,否则复杂度不够,所以要在选到节点u时,向下dfs统计不符合规定的结点数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h>  using  namespace  std;#define  ll long long const  int  INF = 0x3f3f3f3f ;const  ll inf = 0x3f3f3f3f3f3f3f3f ;const  int  N = 2e5  + 10 ;int  n;ll a[N], ans; int  b[N], c[N], fa[N], die[N], cnt[N][2 ];typedef  pair<ll, int >pii;vector<pii>vc; int  st[N], to[N << 1 ], nx[N << 1 ], tot;inline  void  add (int  u, int  v)  void  dfs (int  u, int  _fa)  	fa[u] = _fa; 	if  (b[u] == 0  && c[u] == 1 )cnt[u][0 ] = 1 ; 	if  (b[u] == 1  && c[u] == 0 )cnt[u][1 ] = 1 ; 	for  (int  i = st[u]; i; i = nx[i]) { 		int  v = to[i]; 		if  (v == _fa)continue ; 		dfs (v, u); 		cnt[u][0 ] += cnt[v][0 ]; 		cnt[u][1 ] += cnt[v][1 ]; 	} } int  del (int  u)  	if  (die[u])return  min (cnt[u][0 ], cnt[u][1 ]); 	int  ans = 0 ; 	die[u] = 1 ; 	for  (int  i = st[u]; i; i = nx[i]) { 		int  v = to[i]; 		if  (v != fa[u])ans += del (v); 	} 	return  ans; } int  main ()  	scanf ("%d" , &n); 	for  (int  i = 1 ; i <= n; i++) { 		scanf ("%lld%d%d" , &a[i], &b[i], &c[i]); 		vc.push_back (pii (a[i], i)); 	} 	sort (vc.begin (), vc.end ()); 	for  (int  i = 1 ; i < n; i++) { 		int  u, v; 		scanf ("%d%d" , &u, &v); 		add (u, v); 		add (v, u); 	} 	dfs (1 , 0 ); 	if  (cnt[1 ][0 ] != cnt[1 ][1 ]) { puts ("-1" ); return  0 ; } 	for  (pii p : vc) { 		if  (die[p.second])continue ; 		int  u = p.second; 		int  tmp = min (cnt[u][0 ], cnt[u][1 ]); 		ans += 2  * (tmp - del (u)) * p.first; 	} 	printf ("%lld\n" , ans); 	return  0 ; } 
 
F. Rotating Substrings 
 
题意:给定字符串s,t,每次操作选s的一个子串,把子串的结尾移到子串开头,要使得s=t,问最少操作次数。
dp 
可以看成每次从末尾拿一个,插到前面,使得开头等于t,并逐渐扩大相等的部分。每次从末尾选的字符可以放在任意位置,所以可以先存着,等需要时再放。
d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] s [ i + 1 ⋯ n ] s[i+1\cdots n] s [ i + 1 ⋯ n ] s [ 1 ⋯ i ] s[1\cdots i] s [ 1 ⋯ i ] t [ 1 ⋯ j ] t[1\cdots j] t [ 1 ⋯ j ] 
三种转移
最基本的是把 s [ i ] s[i] s [ i ] s [ i ⋯ n ] s[i\cdots n] s [ i ⋯ n ] s [ 1 ⋯ i − 1 ] s[1\cdots i-1] s [ 1 ⋯ i − 1 ] t [ 1 ⋯ j ] t[1\cdots j] t [ 1 ⋯ j ] 
第二种,如果 s [ i ] = t [ j ] s[i]=t[j] s [ i ] = t [ j ] s [ 1 ⋯ i − 1 ] = t [ 1 ⋯ j − 1 ] s[1\cdots i-1]=t[1\cdots j-1] s [ 1 ⋯ i − 1 ] = t [ 1 ⋯ j − 1 ] 
第三种,把之前存的放到 s [ i + 1 ] s[i+1] s [ i + 1 ] s [ i + 1 ] = t [ j ] s[i+1]=t[j] s [ i + 1 ] = t [ j ] s [ 1 ⋯ i ] = t [ 1 ⋯ j − 1 ] s[1\cdots i]=t[1\cdots j-1] s [ 1 ⋯ i ] = t [ 1 ⋯ j − 1 ] 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <bits/stdc++.h>  using  namespace  std;#define  ll long long const  int  INF = 0x3f3f3f3f ;const  ll inf = 0x3f3f3f3f3f3f3f3f ;const  int  N = 2e5  + 10 ;int  T;char  s[N], t[N];int  cnt[30 ], dp[2020 ][2020 ], ts[N][30 ], ss[N][30 ];bool  ck ()  	memset (cnt, 0 , sizeof  	for  (int  i = 1 ; s[i]; i++)cnt[s[i] - 'a' ]++; 	for  (int  i = 1 ; t[i]; i++)cnt[t[i] - 'a' ]--; 	for  (int  i = 0 ; i < 26 ; i++)if  (cnt[i] != 0 )return  false ; 	return  true ; } int  main ()  	scanf ("%d" , &T); 	while  (T--) { 		int  n; 		scanf ("%d" , &n); 		scanf ("%s%s" , s + 1 , t + 1 ); 		if  (!ck ()) { puts ("-1" ); continue ; } 		memset (ss[n + 1 ], 0 , sizeof 1 ])); 		memset (ts[n + 1 ], 0 , sizeof 1 ])); 		for  (int  i = n; i >= 1 ; i--) { 			memcpy (ss[i], ss[i + 1 ], sizeof  			memcpy (ts[i], ts[i + 1 ], sizeof  			ss[i][s[i] - 'a' ]++; 			ts[i][t[i] - 'a' ]++; 		} 		for  (int  i = 1 ; i <= n; i++) { 			for  (int  j = i; j <= n; j++) { 				dp[i][j] = dp[i - 1 ][j] + 1 ; 				if  (s[i] == t[j]) 					dp[i][j] = min (dp[i][j], dp[i - 1 ][j - 1 ]); 				if  (ss[i + 1 ][t[j] - 'a' ] > ts[j + 1 ][t[j] - 'a' ]) 					dp[i][j] = min (dp[i][j], dp[i][j - 1 ]); 			} 		} 		printf ("%d\n" , dp[n][n]); 	} 	return  0 ; }