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
交互压轴?跑路了。