【游记】CSP2019游记
本来不想写的,结果还是写了(别问我为什么
\(Day1\ T1\)
格雷码,开题发现题面很长,预感不好
问题的本质是提供格雷码的构造方法,求构造有序集合中的第\(K\)个格雷码
开始想着如何去推公式(某凯的疑惑),半个小时没推出来,只好回去想如何去优化模拟
显然我们并不用把前\(K\)个格雷码全部列出来,显然对于\(N\)位格雷码,前\(2^{N-1}\)个格雷码第一位是\(0\),后面的第一位是\(1\)
这样我们可以进行类似线段树查询的递归,时间复杂度\(O(log_2N)\)
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int n;ull k;
void dfs(int now,ull res){
if(!now)return;
ull sum=1ull<<(now-1);
//cout<<now<<" "<<sum<<" "<<res<<endl;
if(res<=sum){printf("0");dfs(now-1,res);}
else{printf("1");dfs(now-1,sum*2-res+1);};
}
int main()
{
cin>>n>>k;
k++;
if(!k){
putchar('1');
for(int i=1;i<64;i++)printf("0");
return 0;
}
dfs(n,k);
return 0;
}
\(Day1\ T2\)
括号树,考场想出正解,见这里
恶臭样例不说了
\(Day1\ T3\)
本来想着\(Day1T3\)人均\(100\),推来推去连部分分都拿不到,
难道爆0滚粗,显然不行
\(DFS\)枚举删边顺序,\(O(N)Check\)一下
结果输入格式错了,调了一个小时不知道哪错了(10分都没了
出考场发现人均\(100+100+10=210\),只有\(100+100+0=200\),貌似差的不是很多(自我安慰一下
\(Day2\ T1\)
背景和题面很神奇(然而我并不知道出处
看题,感觉不是\(DP\)就是组合数学
简化题意
给定一个矩阵,从中选出一些点,使得每行最多选1个,每列选的个数不超过选的总个数的一半,一定要选
每行最多选一个的条件很好满足啊,貌似直接乘法原理可做
然后发现列的条件奇奇怪怪,一下子想不到怎么写,开\(T2\)去了
后话(撑死没想到容斥
重新看题,发现数据范围很奇怪,\(64pts->M\le3\),M这么小,放状压过吗
想半天发现状压不可做,慌,想到出场是人均\(AK\),欸自闭
再暴力点,\(f[i][u][v][k]\)表示\(1-i\)行选了\(u\)个第一列的数,\(v\)个第二列的数,\(k\)个第三列的数,直接枚举每一行选择的数进行转移即可,时间复杂度\(O(N^{M+1})\),\(64pts\)有了
\(Day2\ T2\)
我说这式子怎么这么熟悉啊,这不是斜率优化标准式子啊
直接上\(DP\),\(f[i][j]\)表示上一个分割点在\(i\),这一个分割点在\(j\)
枚举\(k\),\(f[i][j]=min\{f[j][k]+{(sum_i-sum_j)^2}\}\),其中\(sum_i-sum_j>sum_j-sum_k\)
考虑斜率优化,md谁来教我这鬼式子怎么斜率优化啊啊啊啊啊啊
想了半个小时斜率优化无果,考虑改变策略
发现这么方程具有决策单调性,枚举\(j\),再顺序枚举\(i\),\(k\)的取值范围单调递减
这样我们只用两重循环即可(队列都不要
\(O(N^2)\ \ 64pts!\)
\(Day2\ T3\)
题面貌似很复杂,但题意很简单,就是每次切断一条边,得到两个子树,求这些子树的重心的编号和
枚举切的边,然后\(O(N)\)求重心(刚好考前看过
\(O(N^2)\),\(40pts\)
考虑链的情况,显然我们从链的一端开始遍历,\(dfn\)单调递增,这样我们就可以快速求出每次切边后得到的两条新链的中点,而链的中点就是重心
这样就有\(55pts\)
本来想推完全二叉树,没时间了不敢莽
出考场对了一下,发现写的都是对了
这样总分\(100+100+0+64+64+55=383\)
\(luogu\)人均\(100+100+10+100+88+75=473\)
我还是太菜了
周一打电话说tm程序弄丢了(回去重考
就nm离谱
心态炸了,迷迷糊糊进了考场
话说第二次考是正式了点(座位不再随便坐,上个厕所还要登记……
\(Day3\ T1\)
题面短,一眼水题
如果本身是合法日期输出\(0\)
枚举修改一个位置的数,如果能改为合法日期,输出\(1\)
否则输出\(2\)
\(O(1),100pts\)
\(Day3\ T2\)
一看双重\(\sum\)有点慌
分析一下,枚举\(l,r\),\(\sum\)可以用前缀和\(O(1)\)求,这样\(O(N^2)\)就有\(70pts\)
将式子简化\(Ans=\sum^n_{l=1}\sum^n_{r=l}(sumA_r-sumA_{l-1})(sumB_r-sumB_{l-1})\)
然后展开\(Ans=\sum^n_{l=1}\sum^n_{r=l}(sumA_r*sumB_r-sumA_{l-1}*sumB_r-sumB_{l-1}*sumA_r+sumA_{l-1}*sumB_{l-1})\)
分别对\((sumA_{l-1}*sumB_r)\),\((sumB_{l-1}*sumA_r)\),\((sumA_{l-1}*sumB_{l-1})\),记录前缀和
这样我们二次前缀和就解决了问题
\(O(N),100pts\)
注意负数取模
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define P 1000000007
using namespace std;
int n;long long a[500005],b[500005],sa[500005],sb[500005],s[500005];
long long ans=0;
int main(){
scanf("%d",&n);
rep(i,1,n)scanf("%lld",&a[i]),a[i]=(a[i]+a[i-1])%P,sa[i]=(a[i]+sa[i-1])%P;
rep(i,1,n)scanf("%lld",&b[i]),b[i]=(b[i]+b[i-1])%P,sb[i]=(sb[i-1]+b[i])%P;
rep(i,1,n)s[i]=(s[i-1]+a[i]*b[i])%P;
//rep(i,1,n)printf("%d %d %d %d %d\n",a[i],b[i],sa[i],sb[i],s[i]);
rep(i,1,n)ans=((ans+(i*a[i]%P*b[i]%P-a[i]*sb[i-1]%P-sa[i-1]*b[i]%P+s[i-1])%P)%P+P)%P;
printf("%lld\n",ans);
return 0;
}
\(Day3\ T3\)
题面就是给一个奇奇怪怪的网格图,求最小生成树
\(Kruscal\ 64pts\)
剖析\(Kruscal\)的本质,边权相等的边排序后在一起,这样我们对一行/一列的边一起加,记录下已经加过的行和列,避免成环
这样\(O(NlogN),100pts\)
我就这样信心满满走出考场
没开long long
,我太难了
沦为和暴力老哥同分
自闭
\(Day3\ T4\)
真正送我自闭的题目
题面长而毒瘤
目测大模拟
不管先暴力模拟一遍
\(2\ hours\ later...\)
md这第\(3\)个样例是什么东西啊
后来临时突然改样例这是什么操作啊
换了个样例再测还是没过
自闭
换了2个思路,重构了3次还是没模拟出来
真自闭了
还有半个小时才想到还有一道题我,我,我……
\(Day3\ T5\)
众所周知\(wlq\)他炸了
所以这题看完题就没时间了,检查下文件就出考场了
想到可能是并查集加计数\(DP\)
出考场人均\(300+\),然后我没了
\(100+100+100+4+0=304\)
结果\(T3\)没开longlong
\(100+100+64+12=276\)
然后就没有然后了
总结\(Wlq\)太菜了
考试经验不足,考试心态不好,水平有限才是失败的根本原因
改变训练模式,\(CSP2020\)我还回来
补充:机房scw太惨了\(T1\)文件名写错,表示深切的同情