省选模拟6 题解
A. Yist
首先考虑怎样的情况答案是不收敛的。
操作中涉及到对一个权值非$0$,并且不作除法的点的加法贡献。
因为只要最终的答案,可以想到对每个点作为出边的贡献分别处理。
部分分提示求出第一次迭代的贡献,发现对于每个点,贡献都是一个等比数列,所以只要代入求和公式就好了。
然而暴力做的复杂度是$O(mk)$的,会被菊花图的数据卡掉。
考虑如何优化这个东西,根据套路我们将点按照度数分块。
定义度数大于$\sqrt m$的点为重点,度数小于等于$\sqrt m$的点为轻点。
分别讨论:
对于对轻点的操作,因为度数并不大,可以直接暴力。
对于重点的重出边,因为重点个数并不多,可以直接暴力。
对于重点的轻出边,考虑打标记表示这个重点到连接的所有轻点进行了更新操作,修改轻点之前(以及所有操作进行完之后)需要扫重点以累计这个答案。
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int mod=998244353; 5 const int N=2e5+7; 6 const int inv2=499122177; 7 int n,m,k,tot; 8 bool vis[N]; 9 int s[N],head[N],nxt[N],to[N],d[N],sz[N],flag[N]; 10 ll sum[N],tim[N],w[N],t[N]; 11 inline void add(int a,int b){ 12 nxt[++tot]=head[a]; to[head[a]=tot]=b; 13 nxt[++tot]=head[b]; to[head[b]=tot]=a; 14 ++d[a]; ++d[b]; 15 } 16 inline ll qpow(ll x,int k,ll r=1){ 17 for(;k;k>>=1,x=x*x%mod) if(k&1) r=r*x%mod; 18 return r; 19 } 20 vector<int> g[N];//与i临接的重点 21 inline int read(register int x=0,register char ch=getchar(),register int f=0){ 22 for(;!isdigit(ch);ch=getchar()) f=ch=='-'; 23 for(; isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48); 24 return f?-x:x; 25 } 26 int main(){ 27 while(scanf("%d%d%d",&n,&m,&k)==3){ 28 const int sq=sqrt(n)+1; tot=0; 29 memset(vis,0,sizeof(vis)); 30 memset(head,0,sizeof(head)); 31 memset(d,0,sizeof(d)); 32 memset(sz,0,sizeof(sz)); 33 memset(flag,0,sizeof(flag)); 34 memset(sum,0,sizeof(sum)); 35 for(int i=1;i<=n;++i) t[i]=w[i]=read(),tim[i]=1,g[i].clear(); 36 for(int i=1;i<=k;++i) s[i]=read(); 37 for(int i=1;i<=m;++i) add(read(),read()); 38 for(int i=1;i<=n;++i) if(d[i]>=sq) vis[i]=1; 39 for(int i=1;i<=n;++i) for(int j=head[i];j;j=nxt[j]) if(vis[to[j]]) g[i].push_back(to[j]); 40 for(int o=1;o<=k;++o){ 41 int x=s[o]; 42 if(vis[x]){ 43 for(auto to:g[x]) sum[to]+=t[to]; 44 ++flag[x]; 45 tim[x]=tim[x]*inv2%mod; 46 t[x]=t[x]*inv2%mod; 47 } 48 else{ 49 for(int i=head[x];i;i=nxt[i]){ 50 if(vis[to[i]]&&sz[i]<flag[to[i]]) sum[x]+=t[x]*(flag[to[i]]-sz[i]),sz[i]=flag[to[i]]; 51 sum[to[i]]+=t[to[i]]; 52 } 53 tim[x]=tim[x]*inv2%mod; 54 t[x]=t[x]*inv2%mod; 55 } 56 } 57 for(int x=1;x<=n;++x) if(!vis[x]) for(int i=head[x];i;i=nxt[i]) if(vis[to[i]]&&sz[i]<flag[to[i]]) sum[x]+=t[x]*(flag[to[i]]-sz[i]),sz[i]=flag[to[i]]; 58 ll ans=0; int fl=0; 59 for(int i=1;i<=n;++i) if(sum[i]){ 60 sum[i]%=mod; 61 if(tim[i]==1){ fl=1; break; } 62 ans+=sum[i]*qpow(1-tim[i]+mod,mod-2)%mod; 63 } 64 if(fl) puts("-1"); 65 else printf("%lld\n",ans%mod); 66 } 67 return 0; 68 }
B. Ernd
因为看到了出现次数,所以想SAM。
在后面加字符对应着$trans$转移,在前面加字符则对应着向$parent$树上的儿子节点转移。
每个时刻实际上只关注当前的串与那些串能够匹配,而并不是具体的串的情况。
所以设$dp_{x,i}$表示SAM上节点$x$,长度为$i$时的最优策略,然而这个状态数为本质不同的字符串个数,随机数据都过不去。
转移只要讨论三种情况,$trans$转移,$parent$树转移,自转移(保证$i+1<=len_x$)即可。
因为SAM为一个DAG,实际上要求的是一个拓扑图上的最长链,但是可以在一个点停留多次。
根据实际含义理解,字符串在长度小的时候的$endpos$集合大小一定大于长度长的时候。
所以最优策略一定为对于每个节点尽量花完所有的$len$,所以状态数仅为后缀自动机的节点数。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=2e5+7; 4 int n,root=1,cnt=1,lst=1; 5 int ch[N<<1][26],prt[N<<1],len[N<<1],f[N<<1]; 6 char s[N]; 7 long long dp[N<<1]; 8 vector<int> ve[N]; 9 inline void extend(int c){ 10 int np=++cnt,p=lst; lst=np; 11 len[np]=len[p]+1; f[np]=1; 12 ve[len[np]].push_back(np); 13 for(;p&&!ch[p][c];p=prt[p]) ch[p][c]=np; 14 if(!p) prt[np]=root; 15 else{ 16 int q=ch[p][c]; 17 if(len[q]==len[p]+1) prt[np]=q; 18 else{ 19 int nq=++cnt; memcpy(ch[nq],ch[q],sizeof(ch[q])); prt[nq]=prt[q]; 20 len[nq]=len[p]+1; ve[len[nq]].push_back(nq); prt[np]=prt[q]=nq; 21 for(;ch[p][c]==q;p=prt[p]) ch[p][c]=nq; 22 } 23 } 24 } 25 int main(){ 26 scanf("%d%s",&n,s+1); ve[0].push_back(root); 27 for(int i=1;i<=n;++i) extend(s[i]-'a'); 28 for(int i=n;i;--i) for(auto x:ve[i]) f[prt[x]]+=f[x]; 29 long long ans=0; 30 for(int i=0;i<=n;++i) for(auto x:ve[i]){ 31 if(prt[x]) dp[x]=max(dp[x],dp[prt[x]]+1ll*(len[x]-len[prt[x]])*f[x]); 32 for(int j=0;j<26;++j) if(ch[x][j]) dp[ch[x][j]]=max(dp[ch[x][j]],dp[x]+1ll*(len[ch[x][j]]-len[x])*f[ch[x][j]]); 33 ans=max(ans,dp[x]); 34 } 35 printf("%d\n",ans); 36 return 0; 37 }
C. Sanrd
考虑用 拦截导弹 一题的算法,首先预处理出$f$数组,使$f_i$表示强制经过$i$,并且满足LDS长度最大化的LDS方案数。
所以只要在左右两侧分别求LDS,在$i$处合并就好了。
现在只要考虑如何求出一个合法的LIS,在删掉这个LIS之后,求一个LDS就好了。
当一个LIS与所有LDS中的任何一个没有交集,这个LIS就是合法的。
通过颓题解发现一个性质,LIS与LDS的交集大小不超过1。
设最大LDS的方案数为$sum$,根据上述性质,LIS的方案$S$是不合法的仅当$\sum \limits_{i \in S}f_i=sum$。
所以只要求出两个路径上$f$值总和不同的LIS方案,一定有其中一个是合法的,也就可以找到对应的LDS。
所以问题可以通过一个很恶心(记录最大值/两个转移点/两条路径权值和)的树状数组实现。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e5+7; 4 const int mod=998244353; 5 int n,mxd; 6 int p[N],Dp[N],dp[N],f[N],F[N],sum[N],Sum[N],vis[N],PRE[N]; 7 pair<int,int> pre[N],Pre[N]; 8 void dfs(int x,int flag){ 9 if(!x) return ; 10 if(!flag) dfs(pre[x].first,pre[x].second); 11 else dfs(Pre[x].first,Pre[x].second); 12 vis[x]=1; 13 } 14 void Dfs(int x){ 15 if(!x) return ; 16 Dfs(PRE[x]); 17 printf("%d ",x); 18 } 19 int mx[N],fa[N]; 20 pair<int,pair<int,int> > pa[N],pb[N]; 21 inline int lowbit(int x){ 22 return x&-x; 23 } 24 void modify(int x,int val,int sz){ 25 for(;x<=n+1;x+=lowbit(x)) 26 if(val>mx[x]) mx[x]=val,fa[x]=sz; 27 else if(val==mx[x]) (fa[x]+=sz)%=mod; 28 } 29 inline pair<int,int> query(int x){ 30 pair<int,int> r(0,0); 31 for(;x;x^=lowbit(x)){ 32 if(mx[x]>r.first) r.first=mx[x],r.second=fa[x]; 33 else if(mx[x]==r.first) (r.second+=fa[x])%=mod; 34 } 35 return r; 36 } 37 void modify2(int x,int val,int p){ 38 for(;x<=n+1;x+=lowbit(x)) if(val>mx[x]) mx[x]=val,fa[x]=p; 39 } 40 inline pair<int,int> query2(int x){ 41 pair<int,int> r(0,0); 42 for(;x;x^=lowbit(x)) if(mx[x]>r.first) r.first=mx[x],r.second=fa[x]; 43 return r; 44 } 45 int main(){ 46 scanf("%d",&n); 47 for(int i=1;i<=n;++i) scanf("%d",&p[i]); 48 for(int i=1;i<=n;++i){ 49 dp[i]=1; f[i]=1; 50 pair<int,int> d=query(n-p[i]+1); ++d.first; 51 if(d.first>1) dp[i]=d.first,f[i]=d.second; 52 modify(n-p[i]+1,dp[i],f[i]); 53 mxd=max(mxd,dp[i]); 54 } 55 memset(mx,0,sizeof(mx)); 56 memset(fa,0,sizeof(fa)); 57 for(int i=n;i;--i){ 58 Dp[i]=1; F[i]=1; 59 pair<int,int> d=query(p[i]); ++d.first; 60 if(d.first>1) Dp[i]=d.first,F[i]=d.second; 61 modify(p[i],Dp[i],F[i]); 62 } 63 for(int i=1;i<=n;++i){ 64 if(dp[i]+Dp[i]==mxd+1) f[i]=1ll*f[i]*F[i]%mod; 65 else f[i]=0; 66 } 67 p[n+1]=n+1; 68 memset(mx,-1,sizeof(mx)); 69 memset(fa,0,sizeof(fa)); 70 for(int i=1;i<=n+1;++i){ 71 dp[i]=1; sum[i]=f[i]; Sum[i]=-1; 72 for(int x=p[i];x;x^=lowbit(x)){ 73 if(mx[x]+1>dp[i]){ 74 dp[i]=mx[x]+1; 75 sum[i]=(pa[x].first+f[i])%mod; 76 pre[i]=pa[x].second; 77 if(pb[x].first!=-1){ 78 Sum[i]=(pb[x].first+f[i])%mod; 79 Pre[i]=pb[x].second; 80 } 81 else Sum[i]=-1,Pre[i]=make_pair(0,0); 82 } 83 else if(mx[x]+1==dp[i]){ 84 if((pa[x].first+f[i])%mod!=sum[i]) Sum[i]=(pa[x].first+f[i])%mod,Pre[i]=pa[x].second; 85 else if(pb[x].first!=-1&&(pb[x].first+f[i])%mod!=sum[i]) Sum[i]=(pb[x].first+f[i])%mod,Pre[i]=pb[x].second; 86 } 87 } 88 for(int x=p[i];x<=n+1;x+=lowbit(x)){ 89 if(dp[i]>mx[x]){ 90 mx[x]=dp[i]; 91 pa[x].first=sum[i]; 92 pa[x].second=make_pair(i,0); 93 pb[x].first=Sum[i]; 94 pb[x].second=make_pair(i,1); 95 } 96 else if(dp[i]==mx[x]){ 97 if(pa[x].first!=sum[i]){ 98 pb[x].first=sum[i]; 99 pb[x].second=make_pair(i,0); 100 } 101 else if(Sum[i]!=-1&&pa[x].first!=Sum[i]){ 102 pb[x].first=Sum[i]; 103 pb[x].second=make_pair(i,1); 104 } 105 } 106 } 107 } 108 dfs(n+1,0); 109 memset(mx,0,sizeof(mx)); 110 memset(fa,0,sizeof(fa)); 111 int mxv=0,pos=0; 112 for(int i=1;i<=n;++i) if(!vis[i]){ 113 dp[i]=1; 114 pair<int,int> d=query2(n-p[i]+1); ++d.first; 115 if(d.first>1) dp[i]=d.first,PRE[i]=d.second; 116 modify2(n-p[i]+1,dp[i],i); 117 if(dp[i]>mxv) mxv=dp[i],pos=i; 118 } 119 if(mxv==mxd){ 120 printf("%d\n",dp[n+1]-1); 121 for(int i=1;i<=n;++i) if(vis[i]) printf("%d ",i); puts(""); 122 printf("%d\n",mxv); 123 Dfs(pos); 124 } 125 else{ 126 for(int i=1;i<=n;++i) vis[i]=0; 127 if(Sum[n+1]==-1) return puts("-1")&0; 128 dfs(n+1,1); mxv=0; pos=0; 129 memset(mx,0,sizeof(mx)); 130 memset(fa,0,sizeof(fa)); 131 for(int i=1;i<=n;++i) if(!vis[i]){ 132 dp[i]=1; 133 pair<int,int> d=query2(n-p[i]+1); ++d.first; 134 if(d.first>1) dp[i]=d.first,PRE[i]=d.second; 135 modify2(n-p[i]+1,dp[i],i); 136 if(dp[i]>mxv) mxv=dp[i],pos=i; 137 } 138 if(mxv==mxd){ 139 printf("%d\n",dp[n+1]-1); 140 for(int i=1;i<=n;++i) if(vis[i]) printf("%d ",i); puts(""); 141 printf("%d\n",mxv); 142 Dfs(pos); 143 } 144 else return puts("-1")&0; 145 } 146 return 0; 147 }