NOIP,CSP2020游记

第一年参加联赛,可谓吃尽苦头,增长经验了……同时明白了一个惨痛的教训——千万要记得开 long long

CSP 提高组 T2 100->65
NOIP T1 90->30
而这些,全都是因为没有开 long long,或者开了但是忘了写 %lld!

CSP Morning PJ

六号一早进考场,考场里同校的有 xmz,lys,wlo。一看鼠标滚轮坏了,赶紧叫换。

八点半,开题,一看第一题签到,A 了。看到第二题,错看数据范围以为是 Ai≤1e9,突然想到瑞士轮那题,但不记得怎么 O(logn) 归并,于是心慌。最后写了一个二分,但复杂度还是 n 方的。

第三题,一看知道要建树,写了好久,但最后发现数据大了还是出问题,没办法,先去看 T4,显然一遍 dp 做不了,就不同方向跑了两遍,但正确性还是不保。

后来考试成绩出来了,第二题手残把一个“+1”删掉了,0分(本可以85),T1 100,T3 10,T4 25,咳。。

CSP Afternoon TG

下午,第一题看完就知道是模拟,而且是很麻烦的模拟,想着先去看后面的题。T2一看就会了,写完后发现数据范围有点大,开了 long long,但忘了把输入、输出的 %d 改成 %lld,功亏一篑啊!又回到 T1,调了好久,但还是差个一天两天,没办法。T3 以为是线段树,维护了一个 tag,但解决不了除法,想着大概只能骗个 5 分 10 分,就去看 T4 了。T4 第一遍没看懂,又看了一遍,感觉还是没法摸清那些蛇是怎么想的,就匆匆地写了个暴力,但自己也知道不可能对,只剩 5 分钟了。检查了文件,没有问题,就提交了。
Results:T1 40; T2 65; T3 5; T4 0;HB1=,全国不知道排到哪里去了

NOIP Morning

调整好心态,再也不能像 CSP PJ 那样把自己急慌了。T1 看完发现就是个拓扑+GCD,轻松写完(然而最悲惨的是,没有开 LL 因为我估摸着结果不会超过 INT,但其实会。。)T2第一次看没思路,去看 T3T4,感觉题出得不错但是很难推导出来,暴力更难写(?!),就回过头去写 T2,这次会了,写了一个O((n/2+n/3+...+n/n)*n)≈O(n^2) 复杂度的,侧最后一个数据点发现要跑成千上万秒,然后发现了每一个前缀的出现奇数次字符数量一定小于等于26就想到可以用桶(吸取CSP PJ 正解教训),这次测最后一个大样例只用跑2s,虽然还是有点慢,但是好多了,估分80。以为能拿180。

实际得分:T1 30 T2 84。真是不开ll见祖宗……血的教训啊!!!蓝勾又灰飞烟灭了。。


upd:2021年拿到了蓝勾,但实力却没提升……


是不是应该补一下题解呢?不合适单独开文章就写在这儿吧

NOIP2020题目选讲

T2字符串匹配

自从那回NOIP考完并在洛谷上开O2把考场代码优化到了84pts后就再也没有去订正这道题,即便后面尝试去做了几次却都没有更进一步的思路。想大概这样下去也不是很好,今天就看了一下题解把它写了。真糟糕,蓝题没做出来

给你小写字符串 \(S\),求有多少种方案将 \(S\) 写作 \((AB)^iC\),其中 \(A,B,C\) 均非空,并且方案要满足 \(F(A)\le F(C)\)
\(F(S)\) 表示 \(S\) 中出现次数为奇数的字符数量。\(AB\) 表示 \(B\) 接在 \(A\) 后形成的字符串。\(S^i\) 表示 \(S\) 复制 \(i\) 次。
\(n\le 2^{20}\),5 组多测。

顺着最直接的想法走——暴力枚举 \(i\)\((AB)\) 的长度,则 \(C\) 就是确定的。考虑判定 \(F(A)\le F(C)\) 的条件,即在已经确定的前缀 \((AB)\) 中选出一个下标小于它的前缀,使得条件成立。可以预处理前后缀的 \(F\),并随着 \(|AB|\) 的枚举将前缀的 \(F\) 依次加入桶,查询桶里 \(\le F\) 的前缀和,这部分可使用树状数组 \(O(\log 26)\)。枚举 \(i\) 总共是 \(O(\sum_{i=1}^n n/i)=O(n\ln n)\) 的,所以总复杂度 \(O(n\ln n+n\log 26)\),难以通过。

考虑去掉 \(\ln\)。观察到 \(F\) 的值是由奇偶性得出的,存在性质 \((AB)^i\) 对应的 \(F(C)\) = \((AB)^{i+2}\) 对应的 \(F(C)\),因此如果 \((AB)^1\)\((AB)^2\) 都合法则 \(F(C)\) 的值只在两者之间交替变化,分别在第一自然段的计算结果乘上一个计量数即可。这就涉及到 \(AB\)\(S\) 中从头开始连续重复了多少遍,这个怎么求?设 \(num[i]\) 表示前缀 \(i\) 的重复次数,则 \(num[i]=\mathbf{1}[S(1,i)=S(i+1,2i)]\cdot (num[2i]+\mathbf{1}[S(2i\cdot num[2i]+1,2i\cdot num[2i]+i)=S(1,i)])\),这部分用 hash \(O(1)\) 即可。

部分细节见代码。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=(1<<20)+5;
const ull base=37;
int n,f[2][N],cnt[26],buc[27],num[N<<1];
ull ha[N],p[N];
char s[N];
void add(int x){for(x++;x<=27;x+=x&-x)cnt[x-1]++;}
int ask(int x){int s=0;for(x++;x;x-=x&-x)s+=cnt[x-1];return s;}
ull gh(int l,int r){return ha[r]-ha[l-1]*p[r-l+1];}
void sol(){
	memset(cnt,0,sizeof(cnt)),memset(f,0,sizeof(f)),memset(num,0,sizeof(num));
	scanf("%s",s+1),n=strlen(s+1);
	p[0]=1;
	for(int i=1;i<=n;i++)ha[i]=ha[i-1]*base+s[i]-'a',p[i]=p[i-1]*base;
	memset(buc,0,sizeof(buc));
	for(int i=1;i<=n;i++){
		buc[s[i]-'a']++;
		for(int j=0;j<26;j++)f[0][i]+=buc[j]&1;
	}
	memset(buc,0,sizeof(buc));
	for(int i=n;i;i--){
		buc[s[i]-'a']++;
		for(int j=0;j<26;j++)f[1][i]+=buc[j]&1;
	}
	for(int i=n;i;i--){
		if(2*i>n||gh(1,i)!=gh(i+1,2*i)){num[i]=1;continue;}//第一个细节
		num[i]=num[2*i]*2;
		int l=num[2*i]*2*i;
		if(l+i<=n&&gh(l+1,l+i)==gh(1,i))num[i]++;
	}
	ll ans=0;
	for(int i=1;i<n;i++){
		if(i-1)add(f[0][i-1]);
		if(i*num[i]==n)num[i]--;//第二个细节
		ans+=ask(f[1][i+1])*(num[i]+1>>1);
		if(2*i+1<=n)ans+=ask(f[1][2*i+1])*(num[i]>>1);
	}
	cout<<ans<<'\n';
}
int main(){int t;cin>>t;while(t--)sol();}
posted @ 2021-06-30 18:51  pengyule  阅读(85)  评论(0编辑  收藏  举报