多校NOIP24
T1:
据说是第一类斯特林数,然而考场上并没有看出来,却推出了类似的递推式
首先想的是数学题,考虑问题形式类似于错位排列,于是考虑的是钦定若干
位置完全匹配,再计算剩余位置错位排列方案即可,然而是假的,因为无法处理
门后钥匙再次进行匹配的问题
然而这是我找到了问题的子结构,即当门后的钥匙刚好配对时,相当于这次
撞门的机会已经使用完,而当门后钥匙并不配对时,相当于消耗以此撞门机会并
收获一次撞门(开门)机会
考虑设f[i][j]表示当前有i扇门,还有j次撞门机会的方案数,不妨钦定每次撞门
选定1 号门,那么显然分类讨论有:一号门的钥匙配对/不配对,当不配对时相当
于从剩余i-1扇门中选择一扇门,于是有转移:f[i][j] = f[i - 1][j - 1](消耗一次撞门
机会) + (i - 1) * f[i - 1][j](消耗一次收获一次),发现与第一类斯特林数递推公式
相同
T2:
与第一题有些类似,考虑设f[i]表示考虑前i个字符的方案数,转移首先直接继
承i - 1状态,考虑第i个字符的贡献,发现因为只能翻转一次,那么当翻转的子串
不回文时一定会做出一次贡献,那么考虑当前字符,可翻转区间为前i - 1个字符,
依次考虑可以发现,当某字符与当前字符相同时,不会造成贡献,因为在前i - 1个
状态的统计中,其一定已经被计算过(因为字符相同,该位置相当于不翻转),于
是开桶统计字符出现次数,递推转移即可
T3:
明显的图论题,考虑建模的意义,对于一个蛋糕的两种字符,我们将其连边
表示其能相互翻转(转化),那么问题转化为对于一个图,求其最长欧拉路,本
质上就是每条边只能经过一次,求所能经过的最长距离,发现由于只有四个点,
当联通块大小不为4时一定存在一种方案能够遍历所有边,这是显然的,那么并查
集维护联通块权值和即可,当联通块大小为4时,发现对于任意两点之间的若干条
边,我们有两种选择,一是反复横跳遍历所有边,这样根据边数的奇偶会到达两点
中的一点,而假设答案并不在该点,那么我们可以少遍历一条边使得最终到达另一
点,显然贪心的选择不遍历最小的那条边,于是预处理任意两点之间的边数,边权
和以及最小边权,Dfs遍历所有情况即可
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define C char 5 #define B bool 6 #define V void 7 #define S short 8 #define D double 9 #define LL long long 10 #define LD long double 11 #define UI unsigned int 12 #define UL unsigned long long 13 #define P pair<I,I> 14 #define MP make_pair 15 #define a first 16 #define b second 17 #define lowbit(x) (x & -x) 18 #define ot(x) cout << #x << " = " << x << " "; 19 #define debug cout << "It's Ok Here !" << endl; 20 #define FP(x) freopen (#x,"r",stdin) 21 #define FC(x) freopen (#x,"w",stdout) 22 #define Tem template<typename T> 23 // #define memset(name,val,typ,len) memset (name,val,sizeof (typ) * (len)) 24 25 inline I read () { 26 I x (0), y (1); C z (getchar ()); 27 while (!isdigit (z)) { if (z == '-') y = -1; z = getchar (); } 28 while ( isdigit (z)) x = x * 10 + (z & 15), z = getchar (); 29 return x * y; 30 } 31 Tem inline T abs (T &a) { return a >= 0 ? a : -a; } 32 Tem inline V Max (T &a,T b) { a = a > b ? a : b; } 33 Tem inline V Min (T &a,T b) { a = a < b ? a : b; } 34 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; } 35 #ifdef mod 36 Tem inline V Mod1 (T &a,T b) { a = a + b > mod ? a + b - mod : a + b; } 37 Tem inline V Mod2 (T &a,T b) { a = a - b < 0 ? a - b + mod : a - b; } 38 Tem inline T Mod3 (T a,T b) { return a + b > mod ? a + b - mod : a + b; } 39 Tem inline T Mod4 (T a,T b) { return a - b < 0 ? a - b + mod : a - b; } 40 #endif 41 inline P operator + (const P &a,const P &b) { return MP (a.a + b.a,a.b + b.b); } 42 inline P operator - (const P &a,const P &b) { return MP (a.a - b.a,a.b - b.b); } 43 const I N = 5e5 + 3; 44 I n,ans,tmp1,f[4],s[4],t[4]; 45 I a[4][4],b[4][4],c[4][4]; 46 B jud[4]; 47 I get (I x) { 48 return x == f[x] ? 49 x : f[x] = get (f[x]); 50 } 51 V Dfs (I x,I y) { 52 I tmp2 (c[x][y]), tmp3 (a[x][y]); 53 switch (a[x][y] & 1) { 54 case 1 : 55 Max (ans,tmp1 += c[x][y]); 56 c[x][y] = c[y][x] = a[x][y] = a[y][x] = 0; 57 for (I i(0);i < 4; ++ i) if (a[y][i]) 58 Dfs (y,i); 59 a[x][y] = a[y][x] = tmp3, c[x][y] = c[y][x] = tmp2; 60 tmp1 -= c[x][y]; 61 62 if (a[x][y] != 1 && x != y) { 63 tmp1 += c[x][y] - b[x][y], Max (ans,tmp1); 64 c[x][y] = c[y][x] = b[x][y], a[x][y] = a[y][x] = 1; 65 for (I i(0);i < 4; ++ i) if (a[x][i]) 66 Dfs (x,i); 67 a[x][y] = a[y][x] = tmp3, c[x][y] = c[y][x] = tmp2; 68 tmp1 -= c[x][y] - b[x][y]; 69 } 70 break; 71 case 0 : 72 Max (ans,tmp1 += c[x][y]); 73 c[x][y] = c[y][x] = a[x][y] = a[y][x] = 0; 74 for (I i(0);i < 4; ++ i) if (a[x][i]) 75 Dfs (x,i); 76 a[x][y] = a[y][x] = tmp3, c[x][y] = c[y][x] = tmp2; 77 tmp1 -= c[x][y]; 78 79 if (a[x][y] != 1 && x != y) { 80 tmp1 += c[x][y] - b[x][y], Max (ans,tmp1); 81 c[x][y] = c[y][x] = b[x][y], a[x][y] = a[y][x] = 1; 82 for (I i(0);i < 4; ++ i) if (a[y][i]) 83 Dfs (y,i); 84 a[x][y] = a[y][x] = tmp3, c[x][y] = c[y][x] = tmp2; 85 tmp1 -= c[x][y] - b[x][y]; 86 } 87 break; 88 } 89 } 90 signed main () { 91 FP (cake.in), FC (cake.out); 92 n = read (); 93 I w,u,d; 94 memset (b,0x3f,sizeof b); 95 f[0] = 0, f[1] = 1, f[2] = 2, f[3] = 3; 96 t[0] = 1, t[1] = 1, t[2] = 1, t[3] = 1; 97 98 for (I i(1);i <= n; ++ i) { w = read (); 99 u = getchar () - 'W'; getchar (); d = getchar () - 'W'; 100 101 jud[u] = jud[d] = 1; 102 ++ a[u][d], ++ a[d][u]; 103 c[u][d] += w, c[d][u] += w; 104 Min (b[u][d],w), Min (b[d][u],w); 105 106 I fat1 (get (u)), fat2 (get (d)); s[fat1] += w; 107 if (fat1 == fat2) continue; 108 f[fat2] = fat1, s[fat1] += s[fat2], t[fat1] += t[fat2]; 109 } 110 for (I i(0);i < 4; ++ i) if (a[i][i]) 111 a[i][i] >>= 1, c[i][i] >>= 1; 112 for (I i(0);i < 4; ++ i) if (f[i] == i && jud[i]) { 113 switch (t[i]) { 114 case 1 : Max (ans,s[i]); break; 115 case 2 : Max (ans,s[i]); break; 116 case 3 : Max (ans,s[i]); break; 117 case 4 : 118 for (I i(0);i < 4; ++ i) 119 for (I j(0);j < 4; ++ j) if (a[i][j]) 120 Dfs (i,j); 121 break; 122 } 123 } 124 printf ("%d\n",ans); 125 }
注意邻接矩阵存图要考虑自环的决策
T4:
二分题,考场并没有做出来,考虑首先时间一定具有单调性,考虑如何Check
不妨将所有坐标排序,那么对于最左侧的x点一定要遍历其左侧所有y点,归纳可以
得到Check的策略,即现将左侧必须选的卷轴拿到,剩余时间再向右走
注意其可以先向右走一段合法距离再向左拿取所有卷轴