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

原文地址:https://www.cnblogs.com/skyh/p/12188125.html

时间: 2024-10-08 08:55:15

省选模拟6 题解的相关文章

省选模拟五 题解

写在前面: 我好菜啊 伯努利数和自然数幂和的式子都能忘 A. 青蛙 标签: 贪心+二分 题解: 首先我们贪心的让尽量多的青蛙免费跳过去,可以二分求出 考虑剩下的青蛙如何让费用最小: 假如免费的青蛙非零,那么一定可以把中间的石头跳完 这种情况下其他每个青蛙的最小花费一定是一次 另一种情况是没有免费的青蛙 直接让花费最小的青蛙把中间的石头跳一遍,其他的花费仍然都为一次 B. 一起自习的日子 标签: 伯努利数 题解: 我们知道伯努利数有两种$B^{-}$和$B^{+}$ 其中: $$\sum\limi

省选模拟七 题解

写在前面: 这次考试的策略还是蛮正确的 发现了$T2$是水题后先开了$T3$的$60pts$暴力 剩下时间连打带调外加考场刚好用完时间 但可惜的是$T1dp$求两点之间最小代价由于转移出环被弃掉了 其实用$bfs$求最小代价就可以$AC$了 实力不济 就没什么好说的 A. 翻转硬币 标签: $bfs+$状压$dp$ 题解: 先对序列差分 问题转化为每次可以同时异或两个点,求最小代价 同时消去两个点的代价可以用$bfs$预处理出来 源点的个数就是差分序列的$1$的个数 有了这个,状态便有了明显的层

省选模拟13 题解

A. 同桌的你 每个人渴望与一个人当同桌. 容易发现这个关系形成内向基环树森林. 问题转化为求基环树森林的最大匹配. 任意选一条环上的边,分别尝试该边为匹配边.非匹配边即可. B. 大水题 一个常用的但想不到的东西:将每种颜色出现次数的差值为定值,转化为对颜色序列差分后相等. 然后暴力的做法是枚举2^8,表示答案出现在指定的颜色集合中,分别将差分数组插入.查询哈希表即可. 下面是一个优化: 容易发现可行的颜色集合对于同一个时刻只有不超过8种. 对于每一个左端点,随着右端点的右移,区间颜色集合改变

省选模拟22 题解

A. 遮天蔽日 学习了计算几何相关的很多新技巧. 求过一点$P$,圆$O$的切线:通过两点距离.半径,用反三角函数可以解得一个角度,然后就可以算了. 求直线$PQ$,与圆$O$的交点:作点$O$关于$PQ$的垂线,通过半径和垂线长度,可以算得垂足与交点的距离,然后就可以算了. 求多边形的重心:任选一点$O$,选择顺时针(逆时针)上相邻的每个点对$(P,Q)$,对横纵坐标分别加权平均,加的权为面积. 公转:使重心旋转,保持每个点与重心的相对位置不变,整个多边形的朝向不变. 因为打的不是正解,所以伪

省选模拟23 题解

这篇题解鸽了. A. Expectation B. Sequence C. Counting 原文地址:https://www.cnblogs.com/skyh/p/12309632.html

省选模拟35 题解

A. two 考虑现在要通过蓝边删掉红边. 其实等价于要找出有哪些红边,满足恰好只有一个端点在蓝边的儿子方向子树中. 考虑对蓝树跑出一个 $dfs$ 序来,那么问题转化为恰好一个端点在给定区间中. 这像是一个二维偏序问题.考虑以线段树下标为其中的第一维,第二维进行排序处理. 然后用一个 $set$ 就可以简单维护了,但是这样做的复杂度是两个 $log$ 的. 考虑一个特殊的操作,开两棵线段树,第一棵以连接两个点中小的 $dfs$ 序为下标,大的为权值,第二棵则相反. 那么问题就转化为取一段前缀或

省选模拟4 题解

A. 点点的圈圈 因为题中保证的特殊性质,容易发现圆之间的关系形成树形结构. 对于每棵子树,选择树的根或者累计所有子树的答案. 问题在于建图,容易发现这个可以用KDTree优化. 考虑将所有的点建在KDTree上. 用每个点的圆覆盖KDTree,当完全覆盖时直接塞入对应点的vector中. 之后DFS一遍KDTree,同时用一个set维护祖先链上所有的vector的集合. set所表示的集合即能覆盖该点的所有的圆. 对于KDTree中每一个点,直接在set中查后继就可以找到他的父亲. B. 点点

省选模拟11 题解

A. 组合数问题 还没想明白如何做,待补. B. recollection 因为原图为trie树,树上两个点的lcp长度等于两个点的lca深度. 考虑通过广义sam来维护两个点的lcs. 树上同时对应着一个$endpos$,树上两个点对应的$endpos$对应的广义sam上节点在后缀树的lca的$len$即为两个点的lcs长度. 所以对于原树上每个点,我们只关注它的子树在后缀树上能形成的$len$最大的lca. 树上$n$个点形成的lca集合,实际上等于dfs序上相邻的两点形成的lca集合. 所

省选模拟10 题解

A. 食物链 在拓扑序上dp. B. 选点游戏 要求支持合并两棵树,并同时维护树上最大独立集. 考虑特殊情况,每次只加一个标号最大的点,问题是一个简单的动态dp. 离线出最终树的形态,考虑加入的一个叶子节点,更新该节点到1号节点的轻链上信息就可以了. 其实想到这里,一个动态dp的做法已经很显然了,但是考试时仍然没有想到. 仍然维护出原树的形态. 每次的操作是合并两个联通块,也就是合并一对父子. 实际上只要将子节点处的信息更新到父节点直到父节点同连通块的祖先路径上的轻链就可以了. 因为每条重链的链