省选模拟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 }
Yist

 

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 }
Ernd

 

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 }
Sanrd
posted @ 2020-01-13 17:37  skyh  阅读(259)  评论(2编辑  收藏  举报