CSP-S 2019 游记&总结
Day 0:
居然把票买错了。。(RP--)
试机,感觉考试的机房还行(虽然比不上去年的考场)。
考前突然心慌,又看了一会线段树合并和差分约束(事实证明并没有什么用)。
Day 1:
7:40:第一次这么早到考场。
进了考场后先写了一波对拍程序(虽然最后并没有用到)。
T1一眼题。十分钟码完,一发过了大样例就没管了(自信++)。
T2第一眼:这题我好像做过?(并不是)。
开始考虑在序列上的情况,一开始设的是$dp_i$表示前$i$个括号序列的方案数,推了半天没推出来。
设$dp_i$表示以$i$结尾的合法括号序列数,发现可以$O(n)$转移(实际上可以$O(1)$的)。
之后花了1个半小时来想怎么优化这个转移,然而并没有找到可行的方法。
时间只剩半个小时,自己差不多已经意识到凉了。深吸了一口气平复心情,然后看了一眼T3。
嗯?这个T3好像直接贪心就行了啊,好像还有希望。
十分钟后:凉了凉了这个贪心是假的(因为是全删不是选删)。
最后想用十分钟rush出T3的10分暴力,但最后还是没能如愿以偿。
下午在各大平台上自测了一波:
oituku:100+50+0,牛客:100+70+0,luogu:100+95+0
发现T2如果数据水还能多拿点分,心里有了点安慰(虽然别人都是随手切T2)。
突然发现准考证掉考场里了,今年出这么多事果然是要爆炸的征兆。
Day 2:
开题,先看了会T1,发现没思路就直接跳了看T2(一点也不慌)。
T2瞎猜了个结论:记录每个转移点最后一块的大小,然后$n^2$DP即可。
直觉上感觉有点假,但是它轻松的跑过了样例,然后又试着推了一下斜率优化,发现无法处理限制条件(脑袋抽了没想到去考虑决策单调性)。
又去看了一下T3,先写了个40分的暴力。觉得T1有点慌又倒回去看T1,此时大概还有一个半小时。
T1看了一会突然脑补出了一个$O(n^2m)$的DP,四十分钟码完才发现是错的。。突然感觉又回到了$day1$。
最后只能匆匆码完T1的$32$分暴力走人,提交的时候还出了点锅(真是越慌越乱),以后程序里还是最好不要出现中文字符(包括注释)。
32+64+40=136自闭了,考场上还想着64+64+55=183来着。。
下午匆匆退房赶回学校,走的时候差点忘了拿包(多亏同校的同学提醒),看来不只是智力,连记忆也退化了。。
总结:
回学校后仔细想了想今年到底是跪在了哪里。
比起去年,今年没有出现写炸的情况了,时间的分配也还算合理。
感觉自己最大的一个缺点就是在于思维不够严谨,想到一个算法就开始写,写完才发现是错误的,导致浪费了许多时间。
还有一个问题就是容易走进死胡同,总会朝着一个方向使劲钻,往往忽略了其他的思路。
如果$day1 T2$我没有去研究如何优化转移,而是去思考新的转移方程,也许就不会在被卡在$T2$两个半小时导致心态爆炸,最后甚至连T3的10分暴力都写不完。
如果$day2 T1$我能仔细想想那个$dp$方程的正确性,就不会浪费一个小时的时间来码一份错误的代码,也许就有时间写出$64$分或者$84$分的代码,而不是匆匆码完暴力走人。
回校后用民间数据测了一下全省的成绩,贵州的选手真是一年比一年强了。省选加油吧!
补一下题解(不完全):
- Day1 T1:
按照题意模拟,每次可以确定最前面那一位是0还是1,然后递归确定每一位即可。
注意一下$n=64$的情况即可。
- Day1 T2:
考虑在链上的情况:给定一个括号序列,求它的合法子串数目。
设$dp_i$表示以$i$结尾的合法括号序列个数,维护一个栈,遇到右括号时有:$dp_i=dp_{sta[top] - 1} + 1$。然后对dp数组求和即可。
考虑把这个方法搬到树上,我们只需在dfs的过程中维护一个可回退栈,在离开当前节点时把栈顶的元素恢复即可。
- Day1 T3:
差评,不会。
- Day2 T1:
注意到限制1,2都很好解决,我们先考虑没有限制3的情况。
令$sum_i$为第$i$行的总和,则$ans= -1 + \prod_{i=1}^{n}(sum_i + 1)$。(-1是因为不能一道菜也不做)
注意到最多只能有一种菜超过限制,这启发我们枚举每一种超过限制的菜,然后算出不合法方案数进行容斥。
设$dp_{i,j,k}$表示前$i$行,超过限制的列$col$被选了$j$次,其他的被选了$k$次。则不合法方案数为:$\sum_{j>k}f_{n,j,k}$。
注意到我们只关心$j-k$的值,于是可以把后面的两维压成一维,这样就能把复杂度降到$O(mn^2)$(m是枚举每一列的复杂度)。
- Day2 T2:
只会64分的暴力dp。
- Day2 T3:
考虑对于每一个$x$,算出有多少个$u$,满足断开边$(u,fa(u))$后$x$会对答案有贡献。
然后大力分类讨论,可以得到一些结论(下面设$wson$表示$x$的最大子树):
- $x$在$u$的子树中:$2siz_{wson}≤siz_{u}≤2siz_{x}$
- $u$在$x$的某棵子树$v$中:$2siz_{v}-n≤siz_{u}≤min(n-2siz_{wson}, 2siz_{x}-n)$
- $u$不在$x$的子树中,也不是$x$的祖先:$n-2siz_{x}≤siz_{u}≤n-2siz_{wson}$
把这些询问离线下来按照$siz$排序,树状数组维护dfs序即可解决。
这题难就难在细节多而且复杂,具体的维护方式可以见下面的代码。
1 //Day2 T2 centroid
2 #include<bits/stdc++.h>
3 using namespace std;
4
5 inline char Getchar()
6 {
7 static char buf[1000010], *p1 = buf, *p2 = buf;
8 return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
9 }
10
11 inline int read()
12 {
13 int x = 0, f = 1; char s = Getchar();
14 for(; !isdigit(s); s = Getchar()) if(s == '-') f = -1;
15 for(; isdigit(s); s = Getchar()) x = x * 10 + s - 48;
16 return x * f;
17 }
18
19 #define lowbit(x) (x & (-x))
20 typedef long long ll;
21 const int MAXN = 300010;
22
23 struct Edge
24 {
25 int to, nxt;
26 }e[MAXN << 1];
27 int tot, h[MAXN];
28
29 void add(int u, int v)
30 {
31 e[++tot] = (Edge){v, h[u]};
32 h[u] = tot;
33 }
34
35 int T, n, tim, mxl[MAXN << 1], mxr[MAXN << 1];
36 int siz[MAXN], fa[MAXN], wson[MAXN], dfn[MAXN], sum[MAXN];
37
38 void dfs(int x, int f)
39 {
40 dfn[x] = ++tim, fa[x] = f, siz[x] = 1;
41 for(int i = h[x]; i; i = e[i].nxt)
42 {
43 int y = e[i].to;
44 if(y == f) continue;
45 dfs(y, x);
46 siz[x] += siz[y];
47 if(siz[y] > siz[wson[x]]) wson[x] = y;
48 }
49 }
50
51 void get(int pre, int now, int f)
52 {
53 int sz = (e[now].to == f ? 0 : siz[e[now].to]);
54 mxl[now] = max(mxl[pre], sz);
55 if(e[now].nxt) get(now, e[now].nxt, f);
56 mxr[now] = max(mxr[e[now].nxt], sz);
57 }
58
59 struct Fenwick
60 {
61 int tree[MAXN];
62 void modify(int pos, int val) {for(; pos <= n; pos += lowbit(pos)) tree[pos] += val;}
63 int query(int pos) {int res = 0; for(; pos; pos -= lowbit(pos)) res += tree[pos]; return res;}
64 }T1, T2;
65
66 struct Query
67 {
68 int type, val, x, k;
69 bool operator < (const Query &o) const {return k < o.k;}
70 }q[MAXN << 4]; int qcnt;
71
72 struct Data
73 {
74 int x, id;
75 bool operator < (const Data &o) const {return x < o.x;}
76 }d[MAXN];
77
78 int main()
79 {
80 //freopen("centroid.in", "r", stdin);
81 //freopen("centroid.out", "w", stdout);
82 T = read();
83 while(T--)
84 {
85 n = read(); tim = tot = qcnt = 0;
86 for(int i = 1; i <= n; ++i)
87 {
88 h[i] = mxl[i] = mxr[i] = sum[i] = 0;
89 siz[i] = fa[i] = wson[i] = dfn[i] = 0;
90 T1.tree[i] = T2.tree[i] = 0;
91 }
92 for(int i = 1; i < n; ++i)
93 {
94 int u = read(), v = read();
95 add(u, v), add(v, u);
96 }
97 dfs(1, 0);
98 for(int i = 1; i <= n; ++i)
99 {
100 q[++qcnt] = (Query){1, i, i, 2 * siz[i]};
101 q[++qcnt] = (Query){1, -i, i, 2 * siz[wson[i]] - 1};
102 get(0, h[i], fa[i]);
103 for(int j = h[i], pre = 0; j; pre = j, j = e[j].nxt)
104 {
105 int v = e[j].to;
106 if(v == fa[i]) continue;
107 int maxson = max(mxl[pre], mxr[e[j].nxt]);
108 if(2 * siz[v] - n <= min(n - 2 * maxson, 2 * siz[i] - n))
109 {
110 q[++qcnt] = (Query){2, i, v, min(n - 2 * maxson, 2 * siz[i] - n)};
111 q[++qcnt] = (Query){2, -i, v, 2 * siz[v] - n - 1};
112 }
113 }
114 q[++qcnt] = (Query){3, i, n - 2 * siz[i], n - 2 * siz[wson[i]]};
115 q[++qcnt] = (Query){1, -i, fa[i], n - 2 * siz[wson[i]]};
116 q[++qcnt] = (Query){1, i, fa[i], n - 2 * siz[i] - 1};
117 q[++qcnt] = (Query){2, -i, i, n - 2 * siz[wson[i]]};
118 q[++qcnt] = (Query){2, i, i, n - 2 * siz[i] - 1};
119 }
120 for(int i = 2; i <= n; ++i) ++sum[siz[i]];
121 for(int i = 1; i <= n; ++i) sum[i] += sum[i - 1];
122 for(int i = 1; i < n; ++i) d[i] = (Data){siz[i + 1], i + 1};
123 sort(d + 1, d + n);
124 sort(q + 1, q + 1 + qcnt);
125 int j = 1; ll ans = 0;
126 for(int i = 1; i <= qcnt; ++i)
127 {
128 while(j < n && d[j].x <= q[i].k)
129 {
130 T1.modify(dfn[d[j].id], 1);
131 T2.modify(dfn[d[j].id], 1);
132 T2.modify(dfn[d[j].id] + siz[d[j].id], -1);
133 ++j;
134 }
135 int res = 0;
136 if(q[i].type == 3)
137 {
138 q[i].x = max(q[i].x, 1);
139 q[i].k = max(q[i].k, 0);
140 res = sum[q[i].k] - sum[q[i].x - 1];
141 }
142 else if(q[i].type == 1) res = T2.query(dfn[q[i].x]);
143 else res = T1.query(dfn[q[i].x] + siz[q[i].x] - 1) - T1.query(dfn[q[i].x] - 1);
144 ans += (ll)res * q[i].val;
145 }
146 printf("%lld\n", ans);
147 }
148 return 0;
149 }