Good Bye 2020 题解

目前进度:A~G

一万年没开过 vp 了,决定来看一看。

然后开场就降智了,被 BC 连着卡。去看 DEF 发现全是傻逼题,以后被卡题我再不跳我就……就掉分呗(

然后看 G。最近这 CF 都出的什么垃圾题啊,想起来又不难,写又好烦……

甚至还卡空间。出字符串题只开 256MB???一万年后才过。

看起来勉强可以升点分,然而打得这么难受的屑场给我分我也不想要……


A

容易发现答案是 \(x_j-x_i\) 的不同取值个数。

出题人给的那个值域 1e5 的 challenge,我怎么只会 FFT 啊 /kk

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,a[maxn],ans;
bool vis[maxn];
void clear(){
	ans=0;
	FOR(i,0,50) vis[i]=false;
}
void solve(){
	n=read();
	FOR(i,1,n) a[i]=read();
	FOR(i,1,n) FOR(j,i+1,n) if(!vis[a[j]-a[i]]) vis[a[j]-a[i]]=true,ans++;
	printf("%d\n",ans);
	clear();
}
int main(){
	int T=read();
	while(T--) solve();
}

B

最大那个肯定 +1。

次大那个如果 +1 后不等于最大值也要 +1,否则不动。

以此类推。注意相等的数的细节。

出题人给的 challenge 是不是也是尽可能加就行了啊 /fad

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,a[maxn];
void solve(){
	n=read();
	FOR(i,1,n) a[i]=read();
	ROF(i,n,1){
		if(i==n || a[i]+1!=a[i+1] && a[i]!=a[i+1]) a[i]++;
	}
	int ans=1;
	FOR(i,1,n-1) if(a[i]!=a[i+1]) ans++;
	printf("%d\n",ans);
}
int main(){
	int T=read();
	while(T--) solve();
}

C

这都想了这么久是不是退役了?

注意到如果不存在长度为 2 和 3 的回文子串就合法。分奇偶讨论一下显然。

然后无脑冲个 dp。

出题人给的 challenge 似乎可以冲个动态 dp,懒得想了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,f[maxn][2][2];
char s[maxn];
void solve(){
	scanf("%s",s+1);
	n=strlen(s+1);
	FOR(i,0,n) f[i][0][0]=f[i][0][1]=f[i][1][0]=f[i][1][1]=1e9;
	f[0][1][1]=0;
	FOR(i,1,n){
		FOR(a,0,1) FOR(b,0,1) FOR(c,0,1){
			if(a || ((b || s[i]!=s[i-1]) && (c || s[i]!=s[i-2])))
				f[i][a][b]=min(f[i][a][b],f[i-1][b][c]+a);
		} 
	}
	printf("%d\n",min(min(f[n][0][0],f[n][0][1]),min(f[n][1][0],f[n][1][1])));
}
int main(){
	int T=read();
	while(T--) solve();
}

D

首先注意到,同种颜色最优解下是连通的。若不连通,可以只保留最大那个连通块,其它的分给别的颜色。

那么此时一个方案的总代价,是对于每个点,包含在多少个颜色的连通块中,乘上点权的和。

这等价于一个点的邻边中有多少种不同的颜色。这个数不能超过这个点的度数,且至少为 1。

那么每次贪心取最大的就行了。显然可以还原出一种方案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,w[maxn],deg[maxn],tmp[maxn],tl;
ll ans;
void clear(){
	FOR(i,1,n) deg[i]=0;
	tl=ans=0;
}
void solve(){
	n=read();
	FOR(i,1,n) w[i]=read();
	FOR(i,1,2*n-2) deg[read()]++;
	FOR(i,1,n) ans+=w[i];
	printf("%lld ",ans);
	FOR(i,1,n) FOR(j,1,deg[i]-1) tmp[++tl]=w[i];
	sort(tmp+1,tmp+tl+1,greater<int>());
	FOR(i,1,n-2){
		ans+=tmp[i];
		printf("%lld ",ans);
	}
	puts("");
	clear();
}
int main(){
	int T=read();
	while(T--) solve();
}

E

直接枚举 \(j\),然后变成求 \(a_i\&a_j\)\(a_i|a_j\) 的和。

按位讨论,没了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=555555,mod=1000000007;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,sum[maxn];
ll a[maxn];
void solve(){
	n=read();
	FOR(i,1,n) a[i]=read();
	FOR(i,0,59) sum[i]=0;
	FOR(i,1,n) FOR(j,0,59) if((a[i]>>j)&1) sum[j]=(sum[j]+(1ll<<j))%mod;
	int ans=0;
	FOR(i,1,n){
		int s1=0,s2=0;
		FOR(j,0,59) if((a[i]>>j)&1){
			s1=(s1+sum[j])%mod;
			s2=(s2+(1ll<<j)%mod*n)%mod;
		}
		else{
			s2=(s2+sum[j])%mod;
		}
		ans=(ans+1ll*s1*s2)%mod;
	}
	printf("%d\n",ans);
}
int main(){
	int T=read();
	while(T--) solve(); 
}

F

题面完美描述了线性基的构造过程,现在只需要快速模拟即可。

容易发现,任意时候,线性基中的元素,和正在插入的元素(可能已经被消了几次),1 的位数不超过 2。

对于目前在插入的元素,直接找到它的最大位对应的基底。如果不存在直接上去,如果存在就异或一下继续。

然而这个复杂度还是平方级别的。可以被下面这个卡掉:

11000
01100
00110
00011

插入 10000

要解决这个问题也不难。复杂度会被卡是因为异或之后,新多出来的位对应的基底可能也存在。

所以每次加入了基底,就对前面的基底回消。那么每个基底,除了最高位,对应的基底一定不存在。那么每次就只有常数次操作。

要回消的基底数量可能很多,可以用并查集实现。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=555555,mod=1000000007;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
struct vec{
	int len,x[2],id;
	vec(){len=x[0]=x[1]=id=0;}
}a[maxn],b[maxn];
int n,m,tmp[maxn],tl,fa[maxn];
inline int qpow(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
	return ans;
}
inline int getfa(int x){
	return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
int main(){
	n=read();m=read();
	FOR(i,1,n){
		a[i].len=read();
		FOR(j,0,a[i].len-1) a[i].x[j]=read();
		sort(a[i].x,a[i].x+a[i].len,greater<int>());
		a[i].id=i;
	}
	FOR(i,1,m) fa[i]=i;
	FOR(i,1,n){
		while(a[i].len){
//			printf("try %d %d %d\n",a[i].len,a[i].x[0],a[i].x[1]);
			int now=a[i].x[0];
			if(!b[now].len){
				b[now]=a[i];
				break;
			}
			a[i].x[0]=a[i].x[1];
			a[i].x[1]=0;
			a[i].len--;
			if(b[now].len==2){
				a[i].len++;
				a[i].x[a[i].len-1]=getfa(b[now].x[1]);
				sort(a[i].x,a[i].x+a[i].len,greater<int>());
				if(a[i].len==2 && a[i].x[0]==a[i].x[1]) a[i]=vec();
			}
		}
		if(a[i].len==2){
			int now=a[i].x[0],to=a[i].x[1];
			fa[getfa(now)]=to;
		}
//		printf("a[i].len=%d,getfa(2)=%d\n",a[i].len,getfa(2));
	}
	FOR(i,1,m) if(b[i].len) tmp[++tl]=b[i].id;
	sort(tmp+1,tmp+tl+1);
	printf("%d %d\n",qpow(2,tl),tl);
	FOR(i,1,tl) printf("%d ",tmp[i]); 
}
/*
3 3
2 2 3
2 1 2
2 3 1
*/

看了官方题解,发现似乎建个图就不用考虑那么多东西了?

(话说只有两个位我还没想到建图,那我可真是没救了


G

先求出所有长度 \(\le 10^6\)\(s_i\)(设为 \(s_0\dots,s_{mx}\)),显然 \(mx=O(\log)\),且长度和也为线性。

询问串 \(w\)\(s_i\) 出现多少次,如果 \(|s_i|\le 10^6\) 就随便搞了,我选择了离线后 AC 自动机。然后就卡空间卡到自闭。

否则,有两种可能:\(w\) 跨过 \(t_{i-1}\) 或者 \(w\) 完全在 \(s_{i-1}\) 中出现。

这可以用 \(w\) 跨过 \(t_j(mx<j\le i-1)\) 的次数和 \(w\) 完全在 \(s_{mx+1}\) 中出现的次数表示。后者也是离线后 AC 自动机。

前者直接哈希,看 \(w\) 的前缀和 \(s_{mx+1}\) 的后缀是否一样。然后求个前缀和。(然后你发现又要一个 \(n|\Sigma|\) 的数组,慢慢卡吧)

居然写上 4k 了。

官方题解好像没这么垃圾,到时候研究一下,写就算了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1111111,mod=1000000007,bs1=61,bs2=251,mod1=1004535809,mod2=999911659,hhh=1000000;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,q,mx,len[22],ans[maxn],cnt,id[maxn],fail[maxn],que[maxn],h,r,sss[maxn],x[maxn],fl,ch[maxn][26];
int pre1[maxn*2],pre2[maxn*2],suf1[maxn*2],suf2[maxn*2];
char s0[maxn],s[maxn*2],t[maxn],str[maxn*2];
bool ok[maxn];
struct fuck_the_mle{
	int at,pos,id;
	bool operator <(const fuck_the_mle &f)const{
		if(at!=f.at) return at<f.at;
		return pos<f.pos;
	}
}fff[maxn];
inline int qpow(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
	return ans;
} 
void insert(const char *s,int d){
	int now=0,l=strlen(s+1);
	FOR(i,1,l){
		int p=s[i]-'a';
		if(!ch[now][p]) ch[now][p]=++cnt;//,printf("add %d\n",cnt);
		now=ch[now][p];
	}
	id[d]=now;
}
void build(){
	h=1;r=0;
	FOR(i,0,25) if(ch[0][i]) que[++r]=ch[0][i];
	while(h<=r){
		int now=que[h++];
		FOR(i,0,25) if(ch[now][i]){
			fail[ch[now][i]]=ch[fail[now]][i];
			que[++r]=ch[now][i];
		}
		else ch[now][i]=ch[fail[now]][i];
	}
}
void work(int vs){
//	printf("work %d\n",vs);
	FOR(i,1,cnt) sss[i]=0;
	int now=0,l=strlen(s+1);
	FOR(i,1,l){
		int p=s[i]-'a';
		now=ch[now][p];
		sss[now]++;
//		printf("add at %d\n",now);
	}
	ROF(i,r,1) sss[fail[que[i]]]=(sss[fail[que[i]]]+sss[que[i]])%mod;
//	FOR(i,1,cnt) printf("%d ",sss[i]);
//	puts("");
	int tmp=qpow(2,mod-1-vs);
	FOR(i,1,q) if(vs==min(mx,x[i])) ans[i]=(ans[i]+1ll*sss[id[i]]*tmp)%mod;
//	FOR(i,1,q){
//		printf("%lld ",1ll*ans[i]*qpow(2,x[i])%mod);
//	}
//	puts("");
}
int main(){
	n=read();q=read();
	scanf("%s%s",s0+1,t+1);
	len[0]=strlen(s0+1);
	strcpy(s+1,s0+1);
	FOR(i,1,n){
		mx=i;
		strcpy(str+1,s+1);
		s[len[i-1]+1]=t[i];
		strcat(s+1,str+1);
		len[i]=2*len[i-1]+1; 
		if(len[i]>=hhh) break;
	}
	FOR(i,1,len[mx]){
		pre1[i]=(1ll*pre1[i-1]*bs1+s[i]-'a')%mod1;
		pre2[i]=(1ll*pre2[i-1]*bs2+s[i]-'a')%mod2;
	}
	int pr1=1,pr2=1;
	ROF(i,len[mx],1){
		suf1[i]=(suf1[i+1]+1ll*(s[i]-'a')*pr1)%mod1;
		suf2[i]=(suf2[i+1]+1ll*(s[i]-'a')*pr2)%mod2;
		pr1=1ll*pr1*bs1%mod1;
		pr2=1ll*pr2*bs2%mod2;
	}
/*	FOR(i,mx+1,n){
		FOR(j,0,25) sum[i][j]=sum[i-1][j];
		sum[i][t[i]-'a']=(sum[i][t[i]-'a']+qpow(2,mod-1-i))%mod;
	}*/
	FOR(_,1,q){
		x[_]=read();
		scanf("%s",str+1);
		int l=strlen(str+1);
		insert(str,_);
		if(x[_]<=mx || l>len[mx]) continue; 
		FOR(i,1,l) ok[i]=true;
		int hs1=0,hs2=0;
		FOR(i,1,l-1){
			hs1=(1ll*hs1*bs1+str[i]-'a')%mod1;
			hs2=(1ll*hs2*bs2+str[i]-'a')%mod2;
			ok[i+1]&=hs1==suf1[len[mx]-i+1] && hs2==suf2[len[mx]-i+1];
		}
		hs1=hs2=0;
		int pr1=1,pr2=1;
		ROF(i,l,2){
			hs1=(hs1+1ll*(str[i]-'a')*pr1)%mod1;
			hs2=(hs2+1ll*(str[i]-'a')*pr2)%mod2;
			ok[i-1]&=hs1==pre1[l-i+1] && hs2==pre2[l-i+1];
			pr1=1ll*pr1*bs1%mod1;
			pr2=1ll*pr2*bs2%mod2;
		}
		FOR(i,1,l) if(ok[i]) fff[++fl]=(fuck_the_mle){str[i]-'a',x[_],_};
//		FOR(i,1,l) if(ok[i]) ans[_]=(ans[_]+sum[x[_]][str[i]-'a'])%mod;
	}
	sort(fff+1,fff+fl+1);
	int cur=1;
	FOR(i,0,25){
		while(cur<=fl && fff[cur].at<i) cur++;
		int sum=0;
		FOR(j,mx+1,n){
			if(t[j]-'a'==i) sum=(sum+qpow(2,mod-1-j))%mod;
			while(cur<=fl && fff[cur].at==i && fff[cur].pos<j) cur++;
			while(cur<=fl && fff[cur].at==i && fff[cur].pos==j){
				ans[fff[cur].id]=(ans[fff[cur].id]+sum)%mod;
				cur++;
			}
		}
	}
	build();
	MEM(s,0);
	strcpy(s+1,s0+1);
	work(0);
	FOR(i,1,n){
		strcpy(str+1,s+1);
		s[len[i-1]+1]=t[i];
		strcat(s+1,str+1);
		len[i]=2*len[i-1]+1;
//		printf("%s\n",s+1);
		work(i);
		if(len[i]>=hhh) break;
	}
	FOR(i,1,q){
		ans[i]=1ll*ans[i]*qpow(2,x[i])%mod;
		printf("%d\n",ans[i]);
	}
}

H

看都没看。这么长的题面先咕着。

I

交互压轴?跑路了。

posted @ 2021-01-01 13:09  ATS_nantf  阅读(237)  评论(0编辑  收藏  举报