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$的最大子树):

  1. $x$在$u$的子树中:$2siz_{wson}≤siz_{u}≤2siz_{x}$
  2. $u$在$x$的某棵子树$v$中:$2siz_{v}-n≤siz_{u}≤min(n-2siz_{wson}, 2siz_{x}-n)$
  3. $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 }

 

posted @ 2019-11-18 22:31  Aegir  阅读(238)  评论(0编辑  收藏  举报