马上省赛,为了应付一下这个考试,得看看往年题,总结一下经验。
 
B. Build Roads 一个王国,有n个城市。想建n-1条边连接城市,第i个城市有一个建筑公司经验值为ai。想在i和j城市之间建一条路,需要两个城市的建筑公司互相竞争,在修路中两家公司会起冲突,从而造成材料浪费。浪费材料为gcd(ai,aj)
请设计一条路连接n个城市用n条边,花费最少。
1 2 3 int  gen ()  {		    return  xorshitf64() % (R - L + 1 ) + L; } 
 
分析:如果R = L,那么ai = L,相邻城市之间花费L,总花费L*(n-1)。如果R ≠  L,n较小时暴力连边建生成树(n^2);如果n较大,若R - L + 1较大,根据素数分布,素数相距大概几百左右,这样L到(R - L + 1) + L之间必然有一个素数,有一个数为素数,gcd就为1,答案为(n - 1),若R - L + 1较小,R - L + 1区间之内会被取满,根据连续的两个数必然互质可得结果仍然为(n - 1)。
暴力建树,单向建边,取其中任意n-1条边一定会把n个城市给连在一起。所以不需要并查,直接排序依据最小权取n-1个即可。
1 2 3 4 5 6 7 8 9 vector<int > e; int  ans = 0 ;for  (int  i = 1 ; i <= n; i++) a[i] = gen ();for  (int  i = 1 ; i <= n; i++)     for  (int  j = i + 1 ; j <= n; j++)         e.push_back (__gcd(a[i], a[j])); sort (e.begin (), e.end ());for  (int  i = 0 ; i <= n - 1 ; i++) ans += e[i];
 
1 2 3 4 ans = n - 1 ; ans = (n - 1 ) * L; 
 
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 #include <bits/stdc++.h>  using  namespace  std;using  ull = unsigned  long  long ;ull n, L, R, seed, a[200002 ]; ull xorshift64 ()   {	ull x = seed; 	x ^= x << 13 ; 	x ^= x >> 7 ; 	x ^= x << 17 ; 	return  seed = x; } int  gen ()   {	return  xorshift64 () % (R - L + 1 ) + L; } int  main (void )   {	ull ans = 0 ; 	cin >> n >> L >> R >> seed; 	if  (L == R) ans = (n - 1 ) * L; 	else  if  (n <= 20 ) {         vector<ull> e;         for  (int  i = 1 ; i <= n; i++) a[i] = gen ();         for  (int  i = 1 ; i <= n; i++)              for  (int  j = i + 1 ; j <= n; j++)                 e.push_back (__gcd(a[i], a[j]));         sort (e.begin (), e.end ());         for  (int  i = 0 ; i < n - 1 ; i++) ans += e[i];     } else  {         ans = n - 1 ;     }     cout << ans << "\n" ;     return  0 ; } 
 
C. Cat Virus 一个国家,有很多族可以被视为有根树。一个族包含黑色和白色,改族新生儿可能是黑色,如果一个新生儿是黑色,那么它的后代也是黑色。
给一个整数k,可以有k种方式标记黑色和白色组成家族成员。两种方式肯定有不完全相同的成员,至少有一个黑色,而另一个为白色。
问如何建一个有根树使得方式为k。
设f(u)为染色u及u所有子树的方案树,若将u染成黑色,则只有一种方案,若将u染成白色,则设u的子节点为v,f(u)=proc(f(v))+1,u方案数为子树方案数之积在加上黑色的一种方案。各子树之间互不影响,所以是乘积。
若k为奇数,我们给k弄一个左儿子和右儿子,向右儿子方向走,f(r) = (k - 1) / 2,f(l) = 2, f(u) = f(l) * f(r) + 1,若k为偶数,我们直接让u连接一个子节点,然后到下一层,方案数边成k - 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 #include <bits/stdc++.h>  using  namespace  std;#define  int long long using  ull = unsigned  long  long ;int  k;vector<pair<int , int >> ans; int  res = 0 ;void  dfs (int  idx, int  pw)   { 	if  (pw < 3 ) return ; 	if  (pw == 3 ) { 		ans.push_back (make_pair (idx, idx + 1 )); 		res++; 		return ; 	} 	if  (pw & 1 ) { 		ans.push_back (make_pair (idx, idx + 1 )); 		ans.push_back (make_pair (idx, idx + 2 )); 		res += 2 ; 		dfs (idx + 2 , pw >> 1 ); 	} else  { 		ans.push_back (make_pair (idx, idx + 1 )); 		res++; 		dfs (idx + 1 , pw - 1 ); 	} } signed  main (void )   {	cin >> k; 	if  (k == 2 ) { 		cout << "1"  << "\n" ; return  (0 ); 	} else  if  (k == 3 ) { 		cout << "2\n1 2\n" ; return  (0 ); 	} 	else  dfs (1 , k); 	cout << res + 1  << "\n" ; 	for  (auto  &i: ans) { 		cout << i.first << " "  << i.second << "\n" ; 	} 	return  (0 ); } 
 
D. Dyson Box 一个盒子二维方格,左下角坐标为(0,0),右上角坐标为(2×10 ^ 5,2×10 ^ 5)。
将有n个事件发生,第i次事件时出现一个立方体,左下角坐标为(xi-1,yi-1),右上角坐标为(xi,yi)。
盒子里有两个地心引力的方向,水平和竖直,有一半的可能是水平,剩下一半是竖直。她开始测量所有立方体轮廓的总长。如果引力方向是水平,所有立方体水平向左移动,如果是竖直,则竖直向下移动。
计算每次事件后两种情况(两种情况是指向左移动或向下移动)的轮廓总长。
对于竖直情况,当已经有一块,添加下一块时要减去2,对于旁边的两列,如果旁边两栋大楼更高,就减2。
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 #include  <bits/stdc++.h>  #define  int long long #define  debug(x) cout << "[*]"  << #x << " = "  << x << "\n" ; using namespace std ; const  int  maxn = 2e5  + 5 ;int  n, a[maxn], b[maxn];int  ans_x, ans_y;signed  main (void )  {	cin  >> n; 	while  (n--) { 		int  x, y; 		cin  >> x >> y; 		if  (a[x]) ans_x -= 2 ; 		a[x]++; 		if  (a[x] <= a[x - 1 ]) ans_x -= 2 ; 		if  (a[x] <= a[x + 1 ]) ans_x -= 2 ; 		ans_x += 4 ; 		if  (b[y]) ans_y -= 2 ; 		b[y]++; 		if  (b[y] <= b[y - 1 ]) ans_y -= 2 ; 		if  (b[y] <= b[y + 1 ]) ans_y -= 2 ; 		ans_y += 4 ; 		cout  << ans_x << " "  << ans_y << "\n" ; 	} 	return  (0 ); } 
 
F. Birthday Cake 请帮助Yamabuki分蛋糕,Yamabuki有n种不同的蛋糕,可以用小写拉丁字母表示,表示蛋糕上面有什么,字符串的拼接表示蛋糕的合并。
若AB满足CC结构,(A,B两个字符串拼接后可以拆成两个相同的字符串即满足此规律),若AB满足,则BA也满足,所以枚举A的时候找可以满足条件的B即可。
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 #include  <bits/stdc++.h>  using  namespace  std;const  int  mod[2 ] = {999999929 ,1000000007 };const  int  maxn = 4e5 +5 ;const  int  sz = 233 ;using  ll = long  long ;string a[maxn]; ll Hase[maxn][2 ]; ll fac[maxn][2 ]; map<pair<ll,ll>,int > mp; ll length (ll now, int  small, int  mod)   {	return  (now * sz + small) % mod; } ll fetch (int  l, int  r, int  idx)   {	return  (Hase[r][idx]-Hase[l-1 ][idx]*fac[r-l+1 ][idx]%mod[idx]+mod[idx])%mod[idx]; } int  main ()  {    ios::sync_with_stdio (false );     fac[0 ][0 ] = fac[0 ][1 ] = 1 ;     for (int  i = 1 ;i <= 400000 ;++i){         fac[i][0 ] = fac[i-1 ][0 ]*sz%mod[0 ];         fac[i][1 ] = fac[i-1 ][1 ]*sz%mod[1 ];     }     int  n;     cin>>n;     for (int  i = 1 ;i <= n;++i){         cin>>a[i];     }     sort (a+1 ,a+1 +n,[](const  string&a,const  string &b)->bool {         return  a.size ()<b.size ();     });     ll ans = 0 ;     for (int  i = 1 ;i <= n;++i){         ll val[2 ] = {0 ,0 };         for (int  j = 0 ;j < a[i].size ();++j){         	val[0 ] = length (val[0 ], a[i][j], mod[0 ]);             Hase[j+1 ][0 ] = val[0 ];             val[1 ] = length (val[1 ], a[i][j], mod[1 ]);             Hase[j+1 ][1 ] = val[1 ];         }         ans += mp[make_pair (val[0 ],val[1 ])];         int  len = a[i].size ();         for (int  j = 1 ;j <= len/2 ;++j){             if  (fetch (1 , j, 0 ) == fetch (len - j + 1 , len, 0 ) &&                 fetch (1 , j, 1 ) == fetch (len - j + 1 , len, 1 )) {                 ans += mp[make_pair (fetch (j + 1 , len - j, 0 ), fetch (j + 1 , len - j, 1 ))];             }         }         mp[make_pair (val[0 ],val[1 ])]++;     }     cout<<ans<<endl;     return  0 ; }