[考试反思]0113省选模拟6:过载
真累啊。。。离上次放假也挺久,离下次放假也挺久,离上次放出去玩也挺久,离下次放出去玩还不知道有多久。。。
好累啊。。。大脑基本成天在挂机了。什么也不想干了。。。
持续状态不佳。但是今天运气好,所以考试排名看起来还可以。
T1没认真读题也没看数据范围,以为是送分题,17分钟写完交了。。。然后我就把分送出去了
像个弱智一样。。。但是现在的精神状态的确不太能支持好好做题
幸亏T2是个比较简单的SAM+dp,思维量不大,脑子宕机的时候也能写一写。
(归功于当时给大家讲课时稍微有一点理解,要是其它的板子我可能就歇比了)
而且之前改T3时刚认认真真抄过一次板子所以难得没爆炸。
T3的话,啥也不会,搜索呗,还能怎样?
千万不要老老实实写dp什么的,记搜一发至少分不会低,$n^3$过$1000$也是很正常的事。
但是其实$50000$和$100000$的两个点也挺快就跑出来了但是答案不对,不知道为什么。
这次考试虽说名次显得挺高,还是分差不大,加联赛分之后还是很不稳,不是很满意吧。。。至少T1的确弱智了。。。
所有人估计现在状态都不怎么样,所以也许没什么参考价值?
T1:Yist
题目大意:无向图,操作序列有$k$个点(可重!!!)依次操作,每次得分为操作点相邻点权和,操作后该点权减半。
$40%$的部分分要求一轮操作后的得分。$100%$的分要求无限轮操作后的得分,若为无穷输出$-1$。
$n,m\le 10^5,k \le 2\times 10^5,mod=998244353$
看到数据范围里$k>n$怎么就没想到它会重复???
暴力模拟第一问可以得到24分。
考虑第一问和第二问的区别,第二问在一轮操作之后某些点的点权会减小到$\frac{1}{2},\frac{1}{4},\frac{1}{8}...$倍。
所以在下一轮里它的贡献也会相应的按比例减少。等差数列求和即可。
答案为无限的情况就是有的点贡献答案但是没有被操作。
为什么上面只有24而不是40?因为可重所以持续操作菊花中心你就T飞了。
对于这种复杂度与度数有关的题目,还是那个我想不到的套路:分大小点
每个点上存周围所有小点的贡献。每次贡献答案时暴力枚举周围的大点。每次修改小点的时候暴力枚举周围的点。
大小点按照度数分,以根号为界就差不多了,可以证明复杂度是$O(n\sqrt{m})$的
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 400005 4 #define i2 499122177ll 5 #define mod 998244353 6 int w[S],tw[S],op[S],n,m,k,ord[S],fir[S],l[S],to[S],ec,ans,nt,bp[S],deg[S],FIR[S],L[S],TO[S],EC; 7 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 8 int mo(int x){return x>=mod?x-mod:x;} 9 int main(){//freopen("1.in","r",stdin); 10 while(scanf("%d%d%d",&n,&m,&k)!=EOF){int T=sqrt(m); 11 for(int i=1;i<=n;++i)scanf("%d",&w[i]); 12 for(int i=1;i<=k;++i)scanf("%d",&ord[i]),op[ord[i]]++,w[ord[i]]=mo(w[ord[i]]<<1); 13 for(int i=1;i<=n;++i)w[i]=1ll*w[i]*qp(qp(2,op[i])-1,mod-2)%mod; 14 for(int i=1,a,b;i<=m;++i){ 15 scanf("%d%d",&a,&b); 16 l[++ec]=fir[a];fir[a]=ec;to[ec]=b;deg[a]++; 17 l[++ec]=fir[b];fir[b]=ec;to[ec]=a;deg[b]++; 18 if(op[a]==0&&op[b]!=0)nt=1; 19 if(op[a]!=0&&op[b]==0)nt=1; 20 }if(nt){puts("-1");goto clr;} 21 22 for(int i=1;i<=n;++i)if(deg[i]>T)bp[i]=1; 23 for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])if(bp[to[j]])L[++EC]=FIR[i],FIR[i]=EC,TO[EC]=to[j]; 24 else tw[i]=mo(tw[i]+w[to[j]]); 25 for(int i=1;i<=k;++i){ 26 ans=mo(ans+tw[ord[i]]); 27 for(int j=FIR[ord[i]];j;j=L[j])ans=mo(ans+w[TO[j]]); 28 w[ord[i]]=w[ord[i]]*i2%mod; 29 if(!bp[ord[i]])for(int j=fir[ord[i]];j;j=l[j])tw[to[j]]=mo(tw[to[j]]+mod-w[ord[i]]); 30 } 31 printf("%d\n",ans); 32 clr: 33 ec=ans=nt=EC=0; 34 for(int i=1;i<=n;++i)fir[i]=w[i]=tw[i]=ord[i]=op[i]=bp[i]=deg[i]=FIR[i]=0; 35 } 36 }
T2:Ernd
题意:给定字符串S,你有一个空串每次可以在首尾加入一个字符并累加这个串在S中的出现次数的积分,最大化积分。$n \le 2 \times 10^5$
还能是啥啊(除了hash)放在SAM上跑呗
在字符串首尾加入字符不就是沿SAM走边或跳到某个儿子上嘛?
没了啊。答题情况不很乐观看来是我课讲的太烂了?
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 1111111 4 int pc=1,las=1,ep[S],len[S],f[S],c[26][S],fir[S],l[S],to[S],n,ec;char s[S];long long dp[S]; 5 vector<int>V[S]; 6 void insert(int C){ 7 int p=las,np=las=++pc,q,nq; len[np]=len[p]+1; ep[np]=1; 8 for(;p&&!c[C][p];p=f[p])c[C][p]=np; 9 if(!p){f[np]=1;return;} 10 if(len[q=c[C][p]]==len[p]+1){f[np]=q;return;} 11 nq=++pc; len[nq]=len[p]+1; for(int i=0;i<26;++i)c[i][nq]=c[i][q]; 12 f[nq]=f[q]; f[q]=f[np]=nq; 13 for(;c[C][p]==q;p=f[p])c[C][p]=nq; 14 } 15 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 16 void dfs(int p){for(int i=fir[p];i;i=l[i])dfs(to[i]),ep[p]+=ep[to[i]];} 17 int main(){ 18 scanf("%d%s",&n,s+1); 19 for(int i=1;i<=n;++i)insert(s[i]-'a'); 20 for(int i=2;i<=pc;++i)link(f[i],i); 21 dfs(1); 22 for(int i=1;i<=pc;++i)V[len[i]].push_back(i); 23 for(int I=0;I<n;++I)for(int j=0;j<V[I].size();++j){ 24 int p=V[I][j]; 25 for(int i=fir[p];i;i=l[i])dp[to[i]]=max(dp[to[i]],ep[to[i]]*(len[to[i]]-len[p])+dp[p]); 26 for(int i=0;i<26;++i)if(c[i][p])dp[c[i][p]]=max(dp[c[i][p]],ep[c[i][p]]*(len[c[i][p]]-len[p])+dp[p]); 27 }cout<<dp[V[n][0]]<<endl; 28 }
T3:Sanrd
题意:给定$n$排列,求不相交的$LIS/LDS$。输出具体方案。无解则$-1$。$n\le 5\times 10^5$
遇到$LIS$肯定是$dp$?但是这个很神奇啊。
一个显然但我想不到怎么用的结论:$LIS$和$LDS$最多相交1。
我们维护每个位置有多少$LIS$经过,设为$s_i$。
对于一个$LDS$它的每个位置的$s$之和如果是整个串的$LIS$个数那么就不合法,否则一定可以找到至少一种合法方案。
其中$s$数组的求解需要正反做一遍$dp$。树状数组优化,重载运算符,按需自取。
然后我们求解$LDS$,找到每种$LDS$所经过的位置的$s$之和,并寻求一个不等于全部$LIS$的解。
还是一遍朴素的$dp$。
还是需要树状数组,但是转移规则不一样,要记录是从哪里转移来的方便回溯,再开个新结构体重载运算符。
而且还需要记录$s$的和的至少两种取值(如果两个取值不同那么最多有一个是总$LIS$数,即至少一个合法),对两种取值分别记录转移点。
接下来我们就知道一个存在于合法解中的$LDS$了。
然后对剩下没有被占据的位置再来一边$LIS$,规则依旧不一样,需要记录转移来源即长度。
好像可以和上一个并在一起但是我开了第三种树状数组并重载的第三次运算符。
对于方案数,随机数据都能爆int。所以模数要选的好,实在不行开双模。虽说我只要一个$998244353$就过了。
好写难调。细节不少。其实不是特别难(和前几场的T3比起来)
附$miku$的优质小样例,过了基本就80+分了:7 4 1 5 6 3 7 2。答案是4(2346)3(157)。解好像是唯一的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 555555 4 #define mod 998244353 5 int n,a[S],F[S],Mx,All,fa[S],fb[S],mx,nt[S],NT[S]; 6 int mo(int a){return a>=mod?a-mod:a;} 7 struct info{ 8 int w,p; 9 void operator+=(info b){ 10 if(b.w>w)w=b.w,p=b.p; 11 else if(b.w==w)p=mo(p+b.p); 12 } 13 }t[S],f[S],g[S],B; 14 void chg(int p,info w){for(;p<=n;p+=p&-p)t[p]+=w;} 15 info ask(int p,info a=B){for(;p;p^=p&-p)a+=t[p];return a;} 16 struct Info{ 17 int w,a,b,pa,pb; 18 void operator+=(Info x){ 19 if(x.w>w)w=x.w,a=x.a,b=x.b,pa=x.pa,pb=x.pb; 20 if(x.w==w){ 21 if(a!=b)return; 22 if(x.a!=x.b)a=x.a,b=x.b,pa=x.pa,pb=x.pb; 23 else a=x.a,pa=x.pa; 24 } 25 } 26 }T[S],E,A[S]; 27 void chg(int p,Info w){for(;p<=n;p+=p&-p)T[p]+=w;} 28 Info Ask(int p,Info a=E){for(;p;p^=p&-p)a+=T[p];return a;} 29 struct INFO{ 30 int w,p; 31 void operator+=(INFO x){if(x.w>w)w=x.w,p=x.p;} 32 }_t[S],h[S],R; 33 void chg(int p,INFO w){for(;p<=n;p+=p&-p)_t[p]+=w;} 34 INFO ASK(int p,INFO a=R){for(;p;p^=p&-p)a+=_t[p];return a;} 35 int main(){//freopen("1.in","r",stdin); 36 cin>>n; 37 for(int i=1;i<=n;++i)scanf("%d",&a[i]); 38 for(int i=1;i<=n;++i){f[i]=ask(a[i]);if(!f[i].w)f[i].p=1;f[i].w++;chg(a[i],f[i]);} 39 for(int i=1;i<=n;++i)t[i]=B; 40 for(int i=n;i;--i){g[i]=ask(n+1-a[i]);if(!g[i].w)g[i].p=1;g[i].w++,chg(n+1-a[i],g[i]);} 41 for(int i=1;i<=n;++i)F[i]=1ll*f[i].p*g[i].p%mod; 42 for(int i=1;i<=n;++i)Mx=max(Mx,f[i].w); 43 for(int i=1;i<=n;++i)if(f[i].w+g[i].w-1!=Mx)F[i]=0; 44 // for(int i=1;i<=n;++i)cout<<F[i]<<' ';cout<<endl; 45 for(int i=1;i<=n;++i)if(Mx==f[i].w)All=mo(All+f[i].p); 46 for(int i=1;i<=n;++i)A[i]=Ask(n+1-a[i]),fa[i]=A[i].pa,fb[i]=A[i].pb,A[i].pa=A[i].pb=i, 47 A[i].a=mo(A[i].a+F[i]),A[i].b=mo(A[i].b+F[i]),A[i].w++,chg(n+1-a[i],A[i]); 48 for(int i=1;i<=n;++i)mx=max(mx,A[i].w); 49 int bp=-1,nv; 50 for(int i=1;i<=n;++i)if(A[i].w==mx)if(A[i].a!=All||A[i].b!=All){bp=i;break;} 51 if(bp==-1)return puts("-1"),0; 52 nt[bp]=1; 53 if(A[bp].a!=All)nv=mo(A[bp].a+mod-F[bp]),bp=fa[bp];else nv=mo(A[bp].b+mod-F[bp]),bp=fb[bp]; 54 while(bp){ 55 nt[bp]=1; 56 if(A[bp].a==nv)nv=mo(A[bp].a+mod-F[bp]),bp=fa[bp];else nv=mo(A[bp].b+mod-F[bp]),bp=fb[bp]; 57 } 58 printf("%d\n",Mx); 59 for(int i=1;i<=n;++i)if(!nt[i])h[i]=ASK(a[i]),h[i].w++,fa[i]=h[i].p,h[i].p=i,chg(a[i],h[i]); 60 for(int i=1;i<=n;++i)if(h[i].w==Mx){bp=i;break;} 61 while(bp)NT[bp]=1,bp=fa[bp]; 62 for(int i=1;i<=n;++i)if(NT[i])printf("%d ",i);puts(""); 63 printf("%d\n",mx); 64 for(int i=1;i<=n;++i)if(nt[i])printf("%d ",i);puts(""); 65 }