[考试反思]0223省选模拟29:驻足
时间安排?
看半小时题,修半小时电脑,想半小时题,修半小时路由器,码半小时$T1$,尝试连半小时的网,再研究一小时左右的题,最后的时间在断网中度过。
也就匀活着这场考试就考了两个小时半。能拿这分,实属数据水。
其实$T2$写的不是正解但是也想到了正解,然而时间并不够打于是没有写。
$T3$正解的思路打了一部分,然后最后写的暴力,然而样例稍水,一个细节挂掉了然后就炸没了。
题目类型比较对口味,可能也是比较简单,也就没什么好说的。
T1:circle
大意:竞赛图,给出$k$个点,保证其余所有点形成的是无环的,求最少删几个不是被给出的点能使剩余的图无环。$n,k \le 2000$
竞赛图的优美性质啊。
首先考虑$n-k$个点,它们是个子竞赛图,然而没有环,那么它们的边是有传递性的,有唯一拓扑序,也就是度数排序。
因为剩下的$k$个点不能删而它们也是子竞赛图,所以为了让它们没有环,它们也可以排序。如果不是,那么无解。
所以现在我们有两个有序序列,问最少在其中一个删去几个后能保证它们依然有序。
我们把边当作大小关系,那么我们从小到大考虑$n-k$个点,设之为$q$。对于$k$序列中的点$p_1<p_2$如果有$p_1>q,p_2<q$那么$q$一定会被删除。
所以因为$k$序列一定有序,我们可以找到一个分界点,分界点以前都是小于,之后都是大于。
然后对于$n-k$序列里的点我们从小到大考虑,它的分界点必须严格不降,否则也会矛盾。
所以就是一个$LIS$咯。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 2222 4 int n,k,e[S][S],deg[S],ks[S],ik[S],jd[S],nks[S],dp[S]; 5 int main(){ 6 cin>>n>>k; 7 for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%d",&e[i][j]); 8 for(int i=1;i<=k;++i)scanf("%d",&ks[i]),ik[ks[i]]=1; 9 for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)deg[j]+=(ik[i]==ik[j])*e[i][j]; 10 sort(ks+1,ks+1+k,[](int a,int b){return deg[a]<deg[b];}); 11 for(int i=1;i<=k;++i)if(deg[ks[i]]!=i-1)return puts("impossible"),0; 12 for(int i=1,c=0;i<=n;++i)if(!ik[i])nks[++c]=i; 13 sort(nks+1,nks+n-k+1,[](int a,int b){return deg[a]<deg[b];}); 14 for(int i=1;i<=n-k;++i){int p=nks[i];jd[i]=1; 15 for(int j=1;j<=k;++j)if(e[ks[j]][p])jd[i]=j+1;else break; 16 for(int j=jd[i];j<=k;++j)if(e[ks[j]][p]){jd[i]=0;break;} 17 dp[i]=jd[i]?1:0; 18 } 19 for(int i=1;i<=n-k;++i)if(jd[i])for(int j=1;j<i;++j)if(jd[i]>=jd[j])dp[i]=max(dp[i],dp[j]+1); 20 int mx=0; for(int i=1;i<=n-k;++i)mx=max(mx,dp[i]); 21 mx=n-mx-k; if(mx<k)printf("%d",mx);else puts("impossible"); 22 }
T2:生成膜咒
大意:动态在末尾插入一个字符,每次插入后询问所有子串中,对应的fail树上所有点的深度。$n \le 200000$
$fail$是个鬼嘞。就是$border$个数嘛。
我们插入一个字符,产生的影响就是要计算所有的后缀。
而我们发现每个后缀去掉最后一个字符后,它们就是上一次插入字符时所有的后缀。
那么我们这次新产生的贡献就等于上次产生的贡献+所有后缀的最后一个字符的贡献。
所有后缀的$border$。那但凡出现了两次或更多就都可以作为$border$。不论位置。
除了最新刚刚出现的这一次,那么每出现一次就有$1$贡献。
就是$endpos$大小啊。。。新的贡献就是新节点的祖先链$|endpos_p|(len_p -len_{fa})$
链$endpos$加。链带权求和。
暴跳父亲显然可以但是复杂度当然也不对。
出题人并不毒瘤所以这题没有在线,于是可以树剖提前处理树结构。
如果要在线的话$LCT$维护$endpos$也不知道写过多少次了。
然而今天下午有点事,并没有补。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 555555 4 #define mod 1000000007 5 int f[S],c[S][27],len[S],ep[S],pc=1,lst=1,n,ans,delta;char s[S]; 6 void insert(int C){ 7 int p=lst,np=lst=++pc,q,nq;len[np]=len[p]+1; 8 for(;p&&!c[p][C];p=f[p])c[p][C]=np; 9 if(!p){f[np]=1;goto E;} 10 if(len[q=c[p][C]]==len[p]+1){f[np]=q;goto E;} 11 nq=++pc;len[nq]=len[p]+1;f[nq]=f[q];f[q]=f[np]=nq;ep[nq]=ep[q]; 12 for(int i=0;i<26;++i)c[nq][i]=c[q][i]; 13 for(;c[p][C]==q;p=f[p])c[p][C]=nq; 14 E:for(;np;np=f[np])ep[np]++,delta=(delta+(ep[np]-1ll)*(len[np]-len[f[np]]))%mod; 15 } 16 int main(){ 17 scanf("%d%s",&n,s+1); 18 for(int i=1;i<=n;++i)insert(s[i]-'a'),printf("%d\n",(ans+=delta)%=mod); 19 }
T3:simulate
大意:给一个$012$串,每一轮把所有大于等于$2$的位置$-2$,然后其两侧$+1$,(下标小于$1$或大于$n$则不变)。求不断操作后最终的序列。$n\le 2 \times 10^7$
$nc$哥说这题是傻逼模拟稍加优化。我太菜了完全想不到i怎么稍加优化。。。
所谓的轮肯定是来束缚你的。操作顺序显然对答案没有影响。
从前往后考虑,保证扫到的位置前面都是$0/1$。
对于当前位置进行操作,发现就是把前面最近的一个$0$拉近$1$的距离,并把当前数$-1$后面数$+1$
特别的如果前面就是一个$0$那么就是当前位$-2$,前面的$0$消失,后面$+1$
用栈存$0$的位置,注意下标$0$处有无穷无尽个$0$。然后实现这个过程。。
我也不知道这题想考什么但是我的确就是没有想到。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 21111111 4 int a[S],n,sta[S],top=1,ins[S];char s[S]; 5 int main(){ 6 scanf("%s",s+1);n=strlen(s+1); 7 for(int i=1;i<=n;++i)a[i]=s[i]^48; 8 for(int i=1;i<=n;++i){ 9 while(a[i]>=2){ 10 if(!top)sta[top=1]=0; 11 int t=min(a[i]-1,i-sta[top]-1); 12 if(sta[top]!=i-1)sta[top]+=t,a[i+1]+=t,a[i]-=t; 13 else a[i]-=2,top--,a[i+1]++; 14 }if(!a[i])sta[++top]=i; 15 }for(int i=1;i<=n;++i)a[i]=1;for(int i=1;i<=top;++i)a[sta[i]]=0; 16 for(int i=1;i<=n;++i)s[i]='0'+a[i];puts(s+1); 17 }