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

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

研究半天题解啊。。。

全网几乎唯一的官方做法的题解:链接

别的都是暴力。。。。

要是n=3333暴力就完了。

 

一、问题转化

每个联通块第k大的数,直观统计的话,会枚举每个点作为第k大,看看有多少个连通块。但是这样要点分治。而且在d[x]相同的时候可能算重

所以必须以连通块来统计

考虑在连通块的最浅的点统计

但是第k大不一定是这个点

 

考虑一个套路:用许多次分别统计,最后实际上凑出答案的想法。

而且本题权值范围和n同阶。

 

外层枚举每一个i∈[1,w],DP找到所有“包含大于等于w的点有至少k个”的连通块的个数

所有的i的总和就是答案。每个连通块恰好统计了第k大的数权值的次数

这样就可以在连通块最浅点统计辣。

二、DP

DP就很显然了

外层枚举i∈[1,w],dp[x][j]以x为根的子树,选择了大于等于i的有j个的方案数

卷积型背包转移。注意每个儿子还可以不选。

最后答案是每次i的每个x的dp[x][k~n]的和的和

 

看上去似乎优化到头了。(这时候暴力就可以AC了)

但是,

下面开始神仙思路了:

“DP数组是死的,但是套上高级算法就活了”

既然是一个卷积,不妨考虑用生成函数的思想,再用一些多项式的科技和高级数据结构等等来搞搞事情。

现在dp是一个生成函数,首先有:

(注意,可能x有的时候是树的节点编号,有的时候可能是生成函数里的x变量)

$dp[x]=\Pi (dp[son]+1)*Z$

$Z=x^{[d[x]>=i]}$

是一个多项式,当d[x]>=i的时候,Z是x,否则是0,象征x这个点自己是否能够作为>=i的点。

看上去更差劲了,,先接着往下看

三、整体DP

一个强大的思想,是把许多次DP(比如这里要做w次)一起做,叫做整体DP

一般用线段树合并维护,当前点x的线段树的叶子节点下标y的位置的权值,是第y次dp到x这里的权值

考虑把一次影响贡献到所有的DP中。或者说,我们这次外层枚举x,内层枚举每一个DP

线段树合并的时候,更新一些DP值。

但是,,,

这个题每个x的DP是一个数组,不是一个值,,,对应位置卷积,,线段树合并无能为力。。。

生成函数的卷积太麻烦,我们把它变成点值

最外层(主函数)枚举i=1~N+1,象征要求所有的DP的生成函数在x=i时候的点值的和。(注意是和,因为其实是对于每个x统计连通块的个数)

现在,外层枚举到i,dfs到点cur的线段树中下标为y的叶子的f值的意义是:在第y次DP时候,dp[cur]这个生成函数在x=i时候的点值

现在每个位置不是多项式,而是一个值辣。

而我们还要知道所有位置的点值之和,便于统计最后答案

线段树每个叶子再来一个g:外层枚举到i,dfs到点cur的线段树中下标为y的叶子的g值的意义是:在第y次DP时候,整个子树dp[]这个生成函数在x=i时候的点值之和

四、线段树合并

外层枚举i=1~N+1

先考虑要做什么:

1.x的每个位置先赋值为1

2.dfs下去,合并

3.乘上自己的贡献。前[1,d[x]]乘上i,后[d[x]+1,n]乘上1

4.把自己的f贡献到自己的g

4.有一个:$dp[x]=\Pi (dp[son]+1)*Z$

$dp[son]+1$所以干脆把全局f回溯前全局+1

我们需要用线段树维护对于每个子树的对于每个叶子的变换(维护一会说

(最后在rt统计信息不用pushup,只有pushdown)

线段树合并时候对应位置点值相乘(因为真实的dp[x]生成函数最多n项)

对应位置相乘没法做,但是位置同时乘上一个数可以做。由于区间操作,所以在全区间都是同一个“变换”的时候,直接给x树位置乘上变换即可。

变换是什么?

需要支持:

1.维护f,维护g

2.给f+1,给f乘一个数

3.f加到g上去。

设置一个变换(应该是一个群):(a,b,c,d)

定义乘法:$(a1,b1,c1,d1) × (a2,b2,c2,d2)=(a1×a2,a2×b1+b2,c1+c2*a1,c2*b1+d2+d1)$

定义单位元:

$(1,0,0,0)$

推一推发现,这个变换有结合律。可以支持标记合并。

我们把f放在b位置,g放在d位置,就可以维护了。(暴力讨论维护一定也可以,但是绝对不如这个变换的设计好写)

详见代码。

五、插值部分

我们得到的是点值。ans[i]表示最终的生成函数(把所有点的W次求的生成函数加起来)在x=i时候的点值。

用拉格朗日插值还原,见另一篇博客。[学习笔记]拉格朗日插值

 

六、实现细节:

1.64123相乘爆int,用unsigned int

2.每次可以垃圾回收,所以线段树合并用&,使得y的儿子直接变掉(看代码

 

代码:

#include<bits/stdc++.h>
#define il inline
#define reg register int
#define ui unsigned int 
#define mid ((l+r)>>1)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=1700;
const int mod=64123;
int n,k,w;
int d[N];
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
struct data{
    int a,b,c,d;
    data(){}
    data(int aa,int bb,int cc,int dd){
        a=aa;b=bb;c=cc;d=dd;
    }
    data friend operator *(data A,data B){
        return data((ui)A.a*B.a%mod,((ui)A.b*B.a+B.b)%mod,((ui)B.c*A.a+A.c)%mod,((ui)B.c*A.b+A.d+B.d)%mod);
    }
    void clear(){
        a=1,b=0,c=0,d=0;
    }
};
struct tr{
    int ls,rs;
    data val;
    tr(){val.a=1;val.b=0;val.c=0;val.d=0;ls=rs=0;}
    void clear(){val.a=1;val.b=0;val.c=0;val.d=0;ls=rs=0;}
}t[100*N];
int sta[100*N],top,tot;
int nc(){
    return top?sta[top--]:++tot;
}
int rt[N];
int ans[N];//dian zhi
int b[N];//true xishu 
int f[N];
int c[N];
int inv[mod+10];
void del(int &x){
    if(!x) return;
    if(t[x].ls) del(t[x].ls);
    if(t[x].rs) del(t[x].rs);
    sta[++top]=x;
    t[x].clear();
    x=0;
}
void pushdown(int x){
    if(!t[x].ls) t[x].ls=nc();
    if(!t[x].rs) t[x].rs=nc();
    t[t[x].ls].val=t[t[x].ls].val*t[x].val;
    t[t[x].rs].val=t[t[x].rs].val*t[x].val;
    t[x].val.clear();
}
int query(int x,int l,int r){
    if(l==r) return t[x].val.d;
    pushdown(x);
    int ret=query(t[x].ls,l,mid);
    ret=(ret+query(t[x].rs,mid+1,r))%mod;
    return ret;
}
void upda(int &x,int l,int r,int L,int R,data c){
    if(!x) x=nc();
    if(L<=l&&r<=R){
        t[x].val=t[x].val*c;
        return;
    }
    pushdown(x);
    if(L<=mid) upda(t[x].ls,l,mid,L,R,c);
    if(mid<R) upda(t[x].rs,mid+1,r,L,R,c);
}
int merge(int &x,int &y){
    //if(!x||!y) return x+y;每次都会pushdown产生儿子。所以这里不需要 
    if(!t[x].ls&&!t[x].rs) swap(x,y);
    if(!t[y].ls&&!t[y].rs){
        t[x].val=t[x].val*data(t[y].val.b,0,0,0);
        t[x].val=t[x].val*data(1,0,0,t[y].val.d);
        return x;
    }
    pushdown(x);pushdown(y);
    t[x].ls=merge(t[x].ls,t[y].ls);
    t[x].rs=merge(t[x].rs,t[y].rs);
    return x;
}
void dfs(int x,int fa,int k0){
    upda(rt[x],1,w,1,w,data(0,1,0,0));
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x,k0);
        rt[x]=merge(rt[x],rt[y]);
        del(rt[y]);
    }
    upda(rt[x],1,w,1,d[x],data(k0,0,0,0));
    upda(rt[x],1,w,1,w,data(1,0,1,0));
    upda(rt[x],1,w,1,w,data(1,1,0,0));
}
void Lagelangri(){
    f[0]=1;
    for(reg i=1;i<=n+1;++i){
        for(reg j=n+1;j>=0;--j){
            f[j]=((ui)f[j]*(mod-i)%mod+(j?f[j-1]:0))%mod;
        }
    }
    
    for(reg i=1;i<=n+1;++i){
        ll tmp=1;
        for(reg j=1;j<=n+1;++j){
            c[j]=f[j];
            if(j!=i) {
                if(j<i) tmp=tmp*inv[i-j]%mod;
                else tmp=tmp*(mod-inv[j-i])%mod;
            }
        }
        c[0]=f[0];
        tmp=tmp*ans[i]%mod;
        for(reg j=0;j<=n+1;++j){
            if(!j) c[j]=(mod-(ll)c[j]*inv[i]%mod)%mod;
            else c[j]=(mod-((ll)c[j]-c[j-1]+mod)*inv[i]%mod)%mod;
        } 
        
        for(reg j=0;j<=n;++j){
            b[j]=((ll)b[j]+c[j]*tmp%mod)%mod;
        }
    }
}
int main(){
    rd(n);rd(k);rd(w);
    int x,y;
    for(reg i=1;i<=n;++i){
        rd(d[i]);
    }
    for(reg i=1;i<n;++i){
        rd(x);rd(y);add(x,y);add(y,x);
    }
    for(reg i=1;i<=n+1;++i){
        dfs(1,0,i);
        ans[i]=query(rt[1],1,w);
        del(rt[1]);
    }
    inv[1]=1;
    for(reg i=2;i<=n+1;++i) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
    Lagelangri();
    ll op=0;
    for(reg i=k;i<=n;++i){
        op=(op+b[i])%mod;
    }
    printf("%lld",op);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/2/19 9:45:33
*/

总结:

1.题意转化使得连通块可以直接在最浅的地方统计,模型简单,为后面套高级算法留下可能性

2.生成函数思想+点值,妈妈再也不用担心卷积太复杂!~

3.整体DP,赋值之间大同小异,所以所有DP值一起处理,

4.拉格朗日插值O(n^2)trick

 

神仙啊~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

转载于:https://www.cnblogs.com/Miracevin/p/10402405.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值