[考试反思]0318省选模拟49:调整

稍微调整了一下考试策略。

也大概是吸取了昨天只有一道题有分的经验。不管怎样暴力先满上再说。

 

最近的断网频率略有降低,所以尝试了一下,不再是$10$点之前思考$10$点之后暴写。

于是就大概是边想边写觉得效果好像还不错的样子。

$T1$是个比较好想的数据结构。然后$T3$正解思路少一步奇妙的优化。

$T2$是最后写的,时间仍然不是很充足,少写了一档暴力。

大体来说还凑和吧。

 

T1:Manager

大意:树,点带权,对于每个点,求将其点权修改为$10^5$后,所有节点的子树中位数之和。$n,w \le 10^5$

发现修改一个节点影响的是祖先链。影响也只是若干这样的条件:"如果修改的点的权值小于等于$x$那么答案增加$y$"

$x$就是当前子树第$size/2$小值,$y$就是第$size/2+1$小减去$x$

然后查排名直接主席树,然后按照$dfs$序跑一下用树状数组维护上述条件。时间复杂度$O(nlojn)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 211111
 4 int fir[S],l[S],to[S],ec,tim,v[S],n,rt[S],lc[S<<5],rc[S<<5],w[S<<5],pc,mid[S],nxt[S];
 5 long long ans[S],t[S],Ans;
 6 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
 7 #define md (l+r>>1)
 8 void insert(int&p1,int p2,int val,int l=1,int r=100000){
 9     if(!p1)p1=++pc; w[p1]=w[p2]+1; if(l==r)return;
10     if(val<=md)insert(lc[p1],lc[p2],val,l,md),rc[p1]=rc[p2];
11     else insert(rc[p1],rc[p2],val,md+1,r),lc[p1]=lc[p2];
12 }
13 int ask(int p1,int p2,int rk,int l=1,int r=100000){
14     return l==r?l:(rk>=w[lc[p1]]-w[lc[p2]]?ask(rc[p1],rc[p2],rk-w[lc[p1]]+w[lc[p2]],md+1,r):ask(lc[p1],lc[p2],rk,l,md));
15 }
16 void add(int p,int w){for(;p<=100000;p+=p&-p)t[p]+=w;}
17 long long Ask(int p,long long a=0){for(;p;p^=p&-p)a+=t[p];return a;}
18 void dfs(int p){
19     int r=++tim;insert(rt[r],rt[r-1],v[p]);
20     for(int i=fir[p];i;i=l[i])dfs(to[i]);
21     mid[p]=ask(rt[tim],rt[r-1],tim-r>>1);nxt[p]=ask(rt[tim],rt[r-1],tim-r+2>>1)-mid[p];
22 }
23 void DFS(int p){
24     add(100001-mid[p],nxt[p]);ans[p]=Ans+Ask(100001-v[p]);
25     for(int i=fir[p];i;i=l[i])DFS(to[i]);
26     add(100001-mid[p],-nxt[p]);
27 }
28 int main(){
29     cin>>n;
30     for(int i=1;i<=n;++i)scanf("%d",&v[i]);
31     for(int i=2,f;i<=n;++i)scanf("%d",&f),link(f,i);
32     dfs(1); for(int i=1;i<=n;++i)Ans+=mid[i]; DFS(1);
33     for(int i=1;i<=n;++i)printf("%lld\n",ans[i]);
34 }
View Code

 

T2:GCD再放送

大意:$n$个序列,求你任意将它们连接成一个的所有方案中所有子区间的$gcd$的和。$n,a_i \le 10^5$

首先$gcd$不好处理但是$cd$就好多了。拿莫比乌斯反演或者直接容斥一下就行。

然后我们去枚举$cd$的值,找有多少子区间。

一共就两种情况:在一个序列内,或由多个序列拼接而成。

对于第一种情况可以直接读入时直接统计,因为序列的后缀$gcd$最多只有$log$种。

所以我们只要维护所有$gcd$后缀,每次在末尾加入一个字符即可。这样复杂度是$O(nlogn)$的。

定义整个序列的$cd=x$的序列叫做$all$。不是$all$的序列中,前缀是$cd=x$的叫$pre$,$suf$同理。二者可以同时满足。

对于跨序列的,我们可以在中间插入任意个$all$。而右侧是$pre/all$。左侧是$suf/all$

分类讨论$4$种情况。特殊情况是同一个串同时作为$pre/suf$。或者同一个串同时作为$all$两次。

于是我们只要对每个$x$维护,所有$pre$的总长,$suf$的总长,$all$的数量/总长/总长的平方,一个串同时作为$pre/suf$时两端长的乘积。

对于每种情况再枚举中间用了多少$all$,然后乘上选择的方案数,内外的排列方案数就行了。

在统计上面维护的这些值的时候,默认给自己加的值也会加到自己的约数身上,最后一起下传就行。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 1000000007
 4 #define S 100001
 5 int n,suf[S],pre[S],_all[S],all[S],All[S],fac[S],pre_suf[S],finv[S],a[S],p[S],q[S],ans,rp[S],rq[S],np[S],rt[S],mu[S],pc;
 6 vector<int>dv[S];
 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 gcd(int x,int y){return y?gcd(y,x%y):x;}
 9 int C(int b,int t){return 1ll*fac[b]*finv[t]%mod*finv[b-t]%mod;}
10 int main(){
11     mu[1]=1;
12     for(int i=2;i<S;++i){
13         if(!np[i])p[++pc]=i,mu[i]=-1;
14         for(int j=1;i*p[j]<S;++j)
15             if(i%p[j])np[i*p[j]]=1,mu[i*p[j]]=-mu[i];
16             else {np[i*p[j]]=1;break;}
17     }
18     for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)dv[j].push_back(i);
19     for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)rt[j]=(rt[j]+mod+1ll*j/i*mu[i])%mod;
20     for(int i=fac[0]=1;i<S;++i)fac[i]=1ll*fac[i-1]*i%mod; finv[S-1]=qp(fac[S-1],mod-2);
21     for(int i=S-2;~i;--i)finv[i]=(i+1ll)*finv[i+1]%mod;
22     cin>>n;
23     for(int _=1;_<=n;++_){
24         int k;scanf("%d",&k);
25         for(int i=1;i<=k;++i)scanf("%d",&a[i]);
26         int t=0;
27         for(int i=1;i<=k;++i){
28             q[++t]=a[i];p[t]=i;
29             int rt=0;
30             for(int j=1;j<=t;++j)q[j]=gcd(q[j],a[i]);
31             for(int j=1;j<=t;++j)if(q[j]==q[j-1])rp[rt]=p[j];else rp[++rt]=p[j],rq[rt]=q[j];
32             t=rt; for(int i=1;i<=t;++i)p[i]=rp[i],q[i]=rq[i];
33             for(int j=1;j<=t;++j)ans=(ans+1ll*fac[n]*(p[j]-p[j-1])%mod*q[j])%mod;
34         }
35         
36         int G=0,rg=gcd(a[1],a[k]);
37         for(int i=1;i<=k;++i)G=gcd(G,a[i]);
38         _all[G]++; all[G]=(all[G]+k)%mod; All[G]=(All[G]+1ll*k*k)%mod;
39         for(int i=1,g=0;i<=k;++i){
40             g=gcd(g,a[i]);
41             if(g!=G)pre[g]++;
42             else{pre[G]=(pre[G]+1-i+mod)%mod;break;}
43         }
44         for(int i=k,g=0;i;--i){
45             g=gcd(g,a[i]);
46             if(g!=G)suf[g]++;
47             else{suf[G]=(suf[G]+i-k+mod)%mod;break;}
48         }
49         
50         for(int x=0;x<dv[rg].size();++x){
51             int X=dv[rg][x],pt=1,PT=k;
52             if(G%X==0)continue;
53             while(a[pt+1]%X==0)pt++;
54             while(a[PT-1]%X==0)PT--;
55             pre_suf[X]=(pre_suf[X]+pt*(k-PT+1ll))%mod;
56         }
57     }
58     for(int G=1;G<S;++G){
59         for(int i=G+G;i<S;i+=G)pre[G]=(pre[G]+pre[i])%mod,suf[G]=(suf[G]+suf[i])%mod,_all[G]=(_all[G]+_all[i])%mod,all[G]=(all[G]+all[i])%mod,All[G]=(All[G]+All[i])%mod;
60         for(int i=0;i<_all[G];++i)ans=(ans+1ll*rt[G]*C(_all[G]-1,i)%mod*all[G]%mod*(suf[G]+pre[G])%mod*fac[i]%mod*fac[n-i-1])%mod;
61         for(int i=0;i<=_all[G];++i)ans=(ans+1ll*rt[G]*C(_all[G],i)%mod*((1ll*pre[G]*suf[G]-pre_suf[G]+mod)%mod)%mod*fac[i]%mod*fac[n-i-1])%mod;
62         for(int i=0;i<_all[G]-1;++i)ans=(ans+1ll*rt[G]*C(_all[G]-2,i)%mod*((1ll*all[G]*all[G]-All[G]+mod)%mod)%mod*fac[i]%mod*fac[n-i-1])%mod;
63     }cout<<ans;
64 }
View Code

 

T3:dict

大意:定义新的字典序比较方式,给定排列$p$。依次比较$(A_{p_1},B_{p_1}),(A_{p_2},B_{p_2})...$。第一个不同位置上较小的字典序小。给定$A$求有多少字典序小于$A$的值域$[1,m]$的序列。

要求$A,B$都是严格上升的序列。$n,m \le 2 \times 10^5$

说来说去还是字典序,肯定是要逐位确定。我们假设前$i-1$位都一样然后第$i$位不同。

那么可以考虑暴力,枚举这一位是几,每次已确定的位置讲整个序列断开成若干未知区间。

每个未知区间的方案都是关于值域&长度的一个组合数可以直接计算。于是可以$O(n^3)$了

然后我们发现可以全局存一个所有区间的方案数之积,每次确定一位就是把一个区间拆成两个,除一个乘俩就好了。复杂度下降到$O(n^2)$

然后瓶颈在于枚举填什么数。发现其实类似于启发式分裂的过程每次区间会被划分成两段。

如果我们只扫较短的一段,那么总复杂度就是$O(nlogn)$的。发现如果我们要扫的本来就是较小的那直接扫,否则用总方案数减去短的就是另一半。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 200005
 4 #define mod 998244353
 5 set<int>s; int fac[S],finv[S],inv[S],n,p[S],a[S],m,ans,P;
 6 int C(int b,int t){return b<t||t<0?0:1ll*fac[b]*finv[t]%mod*finv[b-t]%mod;}
 7 int iC(int b,int t){return b<t||t<0?0:1ll*finv[b]*fac[t]%mod*fac[b-t]%mod;}
 8 int cal(int l,int r){return C(a[r]-a[l]-1,r-l-1);}
 9 int ical(int l,int r){return iC(a[r]-a[l]-1,r-l-1);}
10 int main(){
11     scanf("%d%d",&n,&m);
12     fac[0]=inv[1]=fac[1]=finv[1]=finv[0]=1;
13     for(int i=2;i<S;++i)fac[i]=1ll*fac[i-1]*i%mod,inv[i]=mod-mod/i*1ll*inv[mod%i]%mod,finv[i]=finv[i-1]*1ll*inv[i]%mod;
14     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
15     sort(a+1,a+1+n);
16     for(int i=1;i<=n;++i)scanf("%d",&p[i]);
17     a[n+1]=m+1;
18     s.insert(0);s.insert(n+1);
19     P=cal(0,n+1);
20     for(int i=1;i<=n&&P;++i){
21         int L=*(--s.lower_bound(p[i])),R=*s.upper_bound(p[i]),z=a[p[i]];
22         if(a[R]-(R-p[i])-z>z-(a[L]+(p[i]-L)))for(int c=z-1;c>=a[L]+(p[i]-L);--c)a[p[i]]=c,ans=(ans+1ll*P*ical(L,R)%mod*cal(L,p[i])%mod*cal(p[i],R))%mod;
23         else{
24             ans=(ans+P)%mod;
25             for(int c=a[R]-(R-p[i]);c>=z;--c)a[p[i]]=c,ans=(ans+mod-1ll*P*ical(L,R)%mod*cal(L,p[i])%mod*cal(p[i],R)%mod)%mod;
26         }
27         a[p[i]]=z;P=1ll*P*ical(L,R)%mod*cal(L,p[i])%mod*cal(p[i],R)%mod;s.insert(p[i]);
28     }cout<<ans<<endl;
29 }
View Code

 

posted @ 2020-03-18 21:38  DeepinC  阅读(252)  评论(0编辑  收藏  举报