luogu4365 秘密袭击 (生成函数+线段树合并+拉格朗日插值)

Posted ressed

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu4365 秘密袭击 (生成函数+线段树合并+拉格朗日插值)相关的知识,希望对你有一定的参考价值。

求所有可能联通块的第k大值的和,考虑枚举这个值:

$ans=sumlimits_{i=1}^{W}{isumlimits_{S}{[i是第K大]}}$

设cnt[i]为连通块中值>=i的个数

$ans=sumlimits_{i=1}^{W}{isumlimits_{S}{[cnt[i]>=K]-[cnt[i+1]>=K]}}$

$ans=sumlimits_{i=1}^{W}{sumlimits_{S}{[cnt[i]>=K]}}$

于是先考虑树上dp,设f[i][j][k]表示以i为根的连通块中,值>=j的数量为k的情况数

然后$ans=sumlimits_{i=1}^{N}{sumlimits_{j=1}^{W}{sumlimits_{k=K}^{N}{f[i][j][k]}}}$

转移和背包类似,所以这样做是$O(N^2W)$的

考虑使用生成函数优化,设$F[i][j]=sum{f[i][j][k]x^k}$,再设$G[i][j]=sum{F[s][j]},i是s的祖先$

于是转移就变成了$F[i][j]*=(F[s][j]+1),G[i][j]+=G[s][j],G[i][j]+=F[i][j]$,其中s是i的孩子

同时有初值$F[i][j]=(d[i]>=j?x:1)$,答案就是G[1][*]的K~N项系数的和

然后当然不能真的去乘了..

考虑先将F和G用点值表达,最后再插回来

首先枚举x=1..N+1,然后给每个点i开动态开点的线段树维护F[i][j]和G[i][j]的值

然后用线段树合并来做对应位置的相乘和相加

具体来说,我们让线段树上的结点维护一个作用在$(f,g)$上的变换$(a,b,c,d)$,使得最终得到$(af+b,cf+d+g)$

然后也不难得到变换的乘法(有结合律但没有交换律)

然后就可以做了 复杂度我也不会分析 反正有可能跑的比暴力还慢

别忘了回收掉不用的点

 

  1 #include<bits/stdc++.h>
  2 #define pa pair<int,int>
  3 #define CLR(a,x) memset(a,x,sizeof(a))
  4 #define MP make_pair
  5 #define fi first
  6 #define se second
  7 using namespace std;
  8 typedef long long ll;
  9 typedef unsigned long long ull;
 10 typedef unsigned int ui;
 11 typedef long double ld;
 12 const int maxn=1700,maxp=3e6;
 13 const int P=64123;
 14 
 15 inline char gc(){
 16     return getchar();
 17     static const int maxs=1<<16;static char buf[maxs],*p1=buf,*p2=buf;
 18     return p1==p2&&(p2=(p1=buf)+fread(buf,1,maxs,stdin),p1==p2)?EOF:*p1++;
 19 }
 20 inline ll rd(){
 21     ll x=0;char c=gc();bool neg=0;
 22     while(c<0||c>9){if(c==-) neg=1;c=gc();}
 23     while(c>=0&&c<=9) x=(x<<1)+(x<<3)+c-0,c=gc();
 24     return neg?(~x+1):x;
 25 }
 26 
 27 struct Node{
 28     int a,b,c,d;
 29     Node(int _a=1,int _b=0,int _c=0,int _d=0){a=_a,b=_b,c=_c,d=_d;}
 30 }val[maxp];
 31 Node operator *(Node x,Node y){
 32     return Node(1ll*x.a*y.a%P,(1ll*x.b*y.a+y.b)%P,(1ll*x.a*y.c+x.c)%P,(1ll*x.b*y.c+x.d+y.d)%P);
 33 }
 34 
 35 int N,K,W,dan[maxn],eg[maxn*2][2],egh[maxn],ect;
 36 int ch[maxp][2],stk[maxp],sh,rt[maxn];
 37 int yy[maxn];
 38 
 39 inline void adeg(int a,int b){
 40     eg[++ect][0]=b,eg[ect][1]=egh[a],egh[a]=ect;
 41 }
 42 
 43 inline int newnode(){
 44     int p=stk[sh--];
 45     assert(sh>=1);
 46     ch[p][0]=ch[p][1]=0;
 47     val[p]=Node();
 48     return p;
 49 }
 50 
 51 inline void delall(int &p){
 52     if(!p) return;
 53     delall(ch[p][0]);delall(ch[p][1]);
 54     stk[++sh]=p;p=0;
 55 }
 56 
 57 inline void pushdown(int p){
 58     if(!ch[p][0]) ch[p][0]=newnode();
 59     if(!ch[p][1]) ch[p][1]=newnode();
 60     val[ch[p][0]]=val[ch[p][0]]*val[p];
 61     val[ch[p][1]]=val[ch[p][1]]*val[p];
 62     val[p]=Node();
 63 }
 64 
 65 void mul(int &p,int l,int r,int x,int y,Node z){
 66     if(!p) p=newnode();
 67     if(x<=l&&r<=y){
 68         val[p]=val[p]*z;
 69     }else{
 70         int m=(l+r)>>1;pushdown(p);
 71         if(x<=m) mul(ch[p][0],l,m,x,y,z);
 72         if(y>=m+1) mul(ch[p][1],m+1,r,x,y,z);
 73     }
 74 }
 75 
 76 int merge(int &p,int &q){
 77     if(!p||!q) return p|q;
 78     if(!ch[p][0]&&!ch[p][1]) swap(p,q);
 79     if(!ch[q][0]&&!ch[q][1]){
 80         val[p]=val[p]*Node(val[q].b,0,0,val[q].d);
 81         return p;
 82     }
 83     pushdown(p),pushdown(q);
 84     ch[p][0]=merge(ch[p][0],ch[q][0]);
 85     ch[p][1]=merge(ch[p][1],ch[q][1]);
 86     return p;
 87 }
 88 
 89 void dfs(int x,int f,int id){
 90     mul(rt[x],1,W,1,W,Node(0,1,0,0));
 91     for(int i=egh[x];i;i=eg[i][1]){
 92         int b=eg[i][0];if(b==f) continue;
 93         dfs(b,x,id);
 94         merge(rt[x],rt[b]);
 95         delall(rt[b]);
 96     }
 97     mul(rt[x],1,W,1,dan[x],Node(id,0,0,0));
 98     mul(rt[x],1,W,1,W,Node(1,0,1,0));
 99     mul(rt[x],1,W,1,W,Node(1,1,0,0));
100 }
101 
102 int query(int p,int l,int r){
103     if(!p) return 0;
104     if(l==r) return val[p].d;
105     int m=(l+r)>>1;pushdown(p);
106     return (query(ch[p][0],l,m)+query(ch[p][1],m+1,r))%P;
107 }
108 
109 int fpow(int x,int y){
110     int r=1;
111     while(y){
112         if(y&1) r=1ll*r*x%P;
113         x=1ll*x*x%P,y>>=1;
114     }return r;
115 }
116 
117 int l[maxn],tmp[maxn],ans[maxn];
118 void calc(){
119     l[0]=1;
120     for(int i=1;i<=N+1;i++){
121         for(int j=i-1;j>=0;j--){
122             l[j+1]=(l[j+1]+l[j])%P;
123             l[j]=-1ll*i*l[j]%P;
124         }
125     }
126     for(int i=1;i<=N+1;i++){
127         int ib=-fpow(i,P-2);
128         tmp[0]=1ll*l[0]*ib%P;
129         for(int j=1;j<=N;j++){
130             tmp[j]=1ll*(l[j]-tmp[j-1])*ib%P;
131         }
132         int k=0,x=1;
133         for(int j=0;j<=N;j++){
134             k=(1ll*x*tmp[j]+k)%P;
135             x=1ll*x*i%P;
136         }
137         k=1ll*fpow(k,P-2)*yy[i]%P;
138         for(int j=0;j<=N;j++){
139             ans[j]=(1ll*tmp[j]*k+ans[j])%P;
140         }
141     }
142 }
143 
144 int main(){
145     //freopen("","r",stdin);
146     N=rd(),K=rd(),W=rd();
147     for(int i=1;i<=N;i++) dan[i]=rd();
148     for(int i=1;i<N;i++){
149         int a=rd(),b=rd();
150         adeg(a,b);adeg(b,a);
151     }
152     for(int i=1;i<maxp-5;i++) stk[++sh]=i;
153     
154     for(int i=1;i<=N+1;i++){
155         dfs(1,0,i);
156         yy[i]=query(rt[1],1,W);
157         delall(rt[1]);
158     }
159     calc();
160     int a=0;
161     for(int i=K;i<=N;i++) a=(a+ans[i])%P;
162     printf("%d
",(a+P)%P);
163     return 0;
164 }

 

以上是关于luogu4365 秘密袭击 (生成函数+线段树合并+拉格朗日插值)的主要内容,如果未能解决你的问题,请参考以下文章

[九省联考2018]秘密袭击coat 伪·题解

[九省联考2018]秘密袭击coat

bzoj5250 [九省联考2018]秘密袭击coat 树形dp

[loj2473]秘密袭击

[九省联考 2018]秘密袭击coat

[九省联考2018]秘密袭击coat