Codeforces 833D Red-Black Cobweb 邊分治

題意

一顆樹,有邊權,和顏色(紅或黑)
求,所有的路徑中,滿足兩種顏色的個數差不超過少的顏色的兩倍的路徑的權值的乘積
路徑的權值爲經過的邊的權值的乘積

題解

邊分治牛逼!!!

出現了兩個錯誤,一個是,快速冪時,指數不能先取模,不知道爲啥腦子抽筋了…
第二個是,算法有一部分考慮的還不完備,測數據時才發現了漏洞

回到這道題來
求不合法的路徑個數
我更喜歡邊分治,因爲考慮的簡單,只會分成兩個子問題,不需要考慮容斥等
假設路徑的一端預處理出的路徑的顏色個數爲(a1,b1)(a_1,b_1),在另一端枚舉到的路徑爲(a2,b2)(a_2,b_2)
不滿足的情況是
2(a1+a2)<b1+b22*(a_1+a_2)<b_1+b_22(b1+b2)<a1+a22*(b_1+b_2)<a_1+a_2 移項得
2a1b1<b22a22*a_1-b_1<b_2-2*a_22b1a1<a22b22*b_1-a_1<a_2-2*b_2
所以,預處理時存下 2a1b12*a_1-b_12b1a12*b_1-a_1
求解時,需要的就是一個前綴積
但需要注意的一點:我們還要得出個數,就是預處理出的滿足條件的路徑個數,因爲,假設當前路徑的權值爲 cc, 路徑個數爲 tt , 答案爲 ctc^t*前綴積,一開始就是這點忽略了

具體求解時,不需要樹狀數組,不需要CDQ分治,因爲兩側互不影響,直接排序後,二分查找就可以了

這很可能是目前時間 rank1rank1 的原因吧
在這裏插入圖片描述
最後,邊分治牛逼

代碼

#include<bits/stdc++.h>
#define N 200010
#define INF 0x3f3f3f3f
#define eps 1e-5
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<"      :   "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
typedef  pair<int,int> pp;

LL ans,res;
int n,tn,sn,cnt,ct,mn,sz[N],la[N],del[N];
struct node{int from,to,w,t,nxt; }G[N<<1];
struct cc{int x,y,z; };
struct BIT{
    struct tt{
        int x,y;
        bool operator < (const tt z) const{ return x<z.x; }
    }q[N];
    int ls[N],cnt;
    void cls(){cnt=0;}
    void pre(){
        sort(q,q+cnt); 
        for(int i=0;i<cnt;i++) {
            ls[i]=q[i].x; 
            if (i) q[i].y=1ll*q[i-1].y*q[i].y%P; 
        } 
    }
    int get(int x){return lb(ls,ls+cnt,x)-ls; }
    void ins(int x,int y){q[cnt++]=tt{x,y}; }
}T1,T2;
inline void add(int x,int y,int w,int t){G[++cnt]=node{x,y,w,t,la[x]}; la[x]=cnt; }
vector<cc> a[N];

void rebuild(int x,int fa){
    int pre=0;
    for(auto i:a[x]){
        int v=i.x,w=i.y,t=i.z;
        if (v==fa) continue;
        if (!pre){
            add(x,v,w,t),add(v,x,w,t); pre=x;
        }else{
            int k=++tn;
            add(k,v,w,t), add(v,k,w,t);
            add(k,pre,1,2), add(pre,k,1,2);
            pre=k;
        }
        rebuild(v,x);
    }
}

void findct(int x,int fa){
    sz[x]=1;
    for(int i=la[x];i;i=G[i].nxt){
        int v=G[i].to;
        if (del[i>>1]||v==fa) continue;
        findct(v,x);
        sz[x]+=sz[v];
        int tmp=max(sz[v],sn-sz[v]);
        if (tmp<mn){
            ct=i;
            mn=tmp;
        }
    }
}

void gao(int x,int fa,int a,int b,LL c){
    if (x<=n) T1.ins(2*a-b,c),T2.ins(2*b-a,c);
    
    for(int i=la[x];i;i=G[i].nxt)if (!del[i>>1]&&G[i].to!=fa)
        gao(G[i].to,x,a+(G[i].t==0),b+(G[i].t==1),c*G[i].w%P);
}

LL qpow(LL a,LL b){LL res=1; while(b){if (b&1) res=res*a%P; a=a*a%P; b>>=1; } return res; }
void get(int x,int fa,int a,int b,LL c){
    if (x<=n) {
        int t=T1.get(b-2*a);
        res=res*qpow(c,t)%P*(t?T1.q[t-1].y:1)%P;
        t=T2.get(a-2*b);
        res=res*qpow(c,t)%P*(t?T2.q[t-1].y:1)%P;
    }
    for(int i=la[x];i;i=G[i].nxt)if (!del[i>>1]&&G[i].to!=fa)
        get(G[i].to,x,a+(G[i].t==0),b+(G[i].t==1),c*G[i].w%P);
}

void dfs(int x){
    int u=G[x].from,v=G[x].to;
    if (sz[u]<sz[v]) swap(u,v);
    del[x>>1]=1;
    T1.cls(); T2.cls();
    gao(u,-1,0,0,1);
    T1.pre(); T2.pre();
    get(v,-1,G[x].t==0,G[x].t==1,G[x].w);

    int tot=sn;
    sn=tot-sz[v]; mn=INF;
    findct(u,-1);
    if (mn!=INF)dfs(ct);
    sn=sz[v]; mn=INF;
    findct(v,-1);
    if (mn!=INF)dfs(ct);
}

void pre(int x,int fa){
    sz[x]=1;
    for(auto i:a[x])if (i.x!=fa){
        pre(i.x,x);
        sz[x]+=sz[i.x];
        ans=ans*qpow(i.y,1ll*(n-sz[i.x])*sz[i.x])%P;
    }
}

int main(int argc, char const *argv[]){
    sc(n);
    for(int i=1;i<n;i++){
        int x,y,z,w;
        scc(x,y); scc(z,w);
        a[x].pb(cc{y,z,w});
        a[y].pb(cc{x,z,w});
    }
    ans=res=cnt=1;
    pre(1,-1);
    tn=n;      
    rebuild(1,-1);
    sn=tn; mn=INF;
    findct(1,-1);
    dfs(ct);
    printf("%lld\n",ans*qpow(res,P-2)%P);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章