树分治---点分治(挑战p360)

poj1741

题:http://poj.org/problem?id=1741

题意:给定树,和一个树k,问有多少个点对之间的路径是不超过k

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=1e18;
int ans=0,tot,maxx,root;
const int M=1e4+4;
struct node{
    int v,w,nextt;
}e[M<<1];
int head[M],vis[M],sz[M],maxv[M],num,dis[M],n,k;
void addedge(int u,int v,int w){
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nextt=head[u];
    head[u]=tot++;
}
void dfssz(int u,int f){
    sz[u]=1;
    maxv[u]=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;//cout<<"!!"<<endl;
        if(v==f||vis[v])
            continue;
        dfssz(v,u);
        sz[u]+=sz[v];
        maxv[u]=max(maxv[u],sz[v]);
    }
}
void dfsroot(int r,int u,int f){
    maxv[u]=max(maxv[u],sz[r]-sz[u]);
    if(maxv[u]<maxx){
        maxx=maxv[u];
        root=u;
    }
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(vis[v]||v==f)
            continue;
        dfsroot(r,v,u);
    }
}
void dfsdis(int u,int d,int f){
    dis[num++]=d;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v==f||vis[v])
            continue;
        dfsdis(v,d+e[i].w,u);
    }
}
int cal(int u,int d){
    int sum=0;
    num=0;
    dfsdis(u,d,-1);
    sort(dis,dis+num);
    int i=0,j=num-1;
    while(i<j){
        while(dis[i]+dis[j]>k&&i<j){
            j--;
        }
        sum+=j-i;
        i++;
    }
    return sum;
}
void solve(int u){
    maxx=n;
    dfssz(u,-1);
    dfsroot(u,u,-1);
    ans+=cal(root,0);
    
    vis[root]=1;
    for(int i=head[root];~i;i=e[i].nextt){
        int v=e[i].v;
        if(vis[v])
            continue;
        ans-=cal(v,e[i].w);
        solve(v);
    }
}
int main(){
    while(~scanf("%d%d",&n,&k)&&(n+k)){
        tot=0,ans=0;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<n;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        
        solve(1);
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

poj1987

题:http://poj.org/problem?id=1987

和上面一样的题意,不一样的写法

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
using  namespace std;
const int M=2e5+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
char s[2];
ll ans;
int num,n,m;
ll sz[M],maxv[M],dis[M],k;
int maxx,vis[M],head[M],reco,total,tot,root;
struct node{
    int v,nextt;
    ll w;
}e[M<<1];
void addedge(int u,int v,int w){
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nextt=head[u];
    head[u]=tot++;
}
void addedge(int u,int v){
    e[tot].v=v;
    e[tot].nextt=head[u];
    head[u]=tot++;
}

void dfsroot(int u,int f){
    sz[u]=1;
    ll maxson=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            dfsroot(v,u);
            sz[u]+=sz[v];
            maxson=max(maxson,sz[v]);
        }
    }
    maxson=max(maxson,total-sz[u]);
    if(maxson<reco)
        root=u,reco=maxson;
}

void dfsdis(int u,int f,ll d){
    dis[num++]=d;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v==f||vis[v])
            continue;
        dfsdis(v,u,d+e[i].w);
    }
}
ll cal(int u,int f,ll d){
    ll sum=0;
    num=0;
    dfsdis(u,f,d);
    sort(dis,dis+num);
    int i=0,j=num-1;
    while(i<j){
        while(dis[i]+dis[j]>k&&i<j){
            j--;
        }
        sum+=1ll*(j-i);
        i++;
    }
    return sum;
}
void doit(int u,int f){
    ///跨越u的路径和在同一个子树中的路径
    ans+=cal(u,f,0);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
///因为同一个子树中的路径在dfs下去的时候会被算到,所以要减去被预先算过的
        if(v!=f&&!vis[v]){
            ans-=cal(v,u,e[i].w);
        }
    }
}
void solve(int u,int f){
    vis[u]=1;
    doit(u,f);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            total=sz[v];
            reco=inf;
            dfsroot(v,u);
            solve(root,0);
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    memset(head,-1,sizeof(head));
    while(m--){
        int u,v;
        ll w;
        scanf("%d%d%lld%s",&u,&v,&w,s);

        addedge(u,v,w);
        addedge(v,u,w);
    }
    scanf("%lld",&k);
    total=n;
    reco=inf;
    dfsroot(1,0);
    solve(root,0);
    printf("%lld\n",ans);
    return 0;
}
View Code

 题:https://www.luogu.org/problem/P4149

题意:给定固定路径长,边数最少路径

#include<bits/stdc++.h>
using namespace std;
const int M=2e5+5;
const int inf=0x3f3f3f3f;
const int N=1e6+6;
int vis[M],temp[N],sz[M],head[M];
int n,k,total,reco,root;
int ans,tot;
#define pb push_back
struct node{
    int u,v,w,nextt;
}e[M<<1];
void addedge(int u,int v,int w){
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nextt=head[u];
    head[u]=tot++;
}
void dfsroot(int u,int f){
    int maxx=0;
    sz[u]=1;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            dfsroot(v,u);
            sz[u]+=sz[v];
            maxx=max(maxx,sz[v]);
        }
    }
    maxx=max(maxx,total-sz[u]);
    if(reco>maxx)
        reco=maxx,root=u;
}
void cal(int u,int f,int d,int num){
    if(d>k)
        return ;
    ans=min(ans,temp[k-d]+num);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            cal(v,u,d+e[i].w,num+1);
        }
    }
}
void update(int u,int f,int d,int num){
    if(d>k)
        return ;
    temp[d]=min(temp[d],num);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            update(v,u,d+e[i].w,num+1);
        }
    }
}
void Clear(int u,int f,int d){
    if(d>=k)
        return ;
    temp[d]=inf;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            Clear(v,u,d+e[i].w);
        }
    }
}
void dfs1(int u,int f){
    sz[u]=1;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            dfs1(v,u);
        }
    }
}
void doit(int u,int f){
    dfs1(u,f);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            cal(v,u,e[i].w,1);
            update(v,u,e[i].w,1);
        }
    }
    Clear(u,f,0);
}
void solve(int u,int f){
    vis[u]=1;
    temp[0]=0;
    doit(u,f);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            total=sz[v];
            reco=inf;
            dfsroot(v,u);
            solve(root,0);
        }
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=0;i<=n;i++)
        head[i]=-1;
    for(int i=0;i<=k;i++)
        temp[i]=inf;
    for(int u,v,w,i=1;i<n;i++){
        scanf("%d%d%d",&u,&v,&w);
        u++,v++;
        addedge(u,v,w);
        addedge(v,u,w);
    }
    total=n;
    reco=inf;
    ans=inf;
    dfsroot(1,0);
    solve(root,0);
    if(ans==inf)
        puts("-1");
    else
        printf("%d\n",ans);
    return 0;
}
View Code

 题(求点权处理的题):http://acm.hdu.edu.cn/showproblem.php?pid=4812

题意:给定一棵 n 个点的树,每个点有权值 Vi,问是否存在一条路径使得路径上所有点的权值乘积 mod(10^6 + 3) 为 K,输出路径的首尾标号,若有多解,输出字典序最小的解

注意:更新就更新以重点为起点的路径,枚举答案时则用以重点孩子为起点的路径

除法,逆元处理

#pragma comment(linker,"/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define pb push_back
typedef long long ll;
const int M=1e5+5;
const int mod=1e6+3;
const int inf=0x3f3f3f3f;
int reco,total,root,n;
ll k;
ll sz[M],a[M],vis[M];
int ansi,ansj;
int temp[mod+6];
struct node{
    int v,nextt;
}e[M<<1];
int head[M];
int tot;
int inv[mod+6];
void addedge(int u,int v){
    e[tot].v=v;
    e[tot].nextt=head[u];
    head[u]=tot++;
}
void iniInv(){
    inv[1]=1;
    for(int i=2;i<mod;i++)
        inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
}
void dfsroot(int u,int f){
    sz[u]=1;
    ll maxson=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            dfsroot(v,u);
            sz[u]+=sz[v];
            maxson=max(maxson,sz[v]);
        }
    }
    maxson=max(maxson,total-sz[u]);
    if(maxson<reco)
        root=u,reco=maxson;
}
void up(int x,int y){
    int minn=min(x,y);
    int maxx=max(x,y);
    if(minn<ansi){
        ansi=minn,ansj=maxx;
    }
    else if(minn==ansi){
        if(maxx<ansj)
            ansj=maxx;
    }
}
void cal(int u,int f,int d){
    int x=temp[k*inv[d]%mod];
    if(x)
        up(u,x);

    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            cal(v,u,d*a[v]%mod);
        }
    }
}
void update(int u,int f,int d){
    if(!temp[d])
        temp[d]=u;
    else
        temp[d]=min(temp[d],u);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            update(v,u,d*a[v]%mod);
        }
    }
}
void del(int u,int f,int d){
    temp[d]=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            del(v,u,d*a[v]%mod);
        }
    }
}
void doit(int u,int f){
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]){
            cal(v,u,a[v]);
            update(v,u,a[u]*a[v]%mod);
        }
    }
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]){
            del(v,u,a[u]*a[v]%mod);
        }
    }
}
void solve(int u,int f){
    vis[u]=1;
    temp[a[u]]=u;
    doit(u,f);
    temp[a[u]]=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(!vis[v]&&v!=f){
            reco=inf;
            total=sz[v];
            dfsroot(v,u);
            solve(root,0);
        }
    }
}
int main(){
//    freopen("in","r",stdin);
    iniInv();

    while(scanf("%d%lld",&n,&k)!=EOF){
        tot=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),vis[i]=0;
        memset(temp,0,sizeof(temp));

        for(int u,v,i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
        }
        ansi=ansj=inf;
        reco=inf;
        total=n;
        dfsroot(1,0);
        solve(root,0);
        if(ansi==inf){
            puts("No solution");
        }
        else{
            printf("%d %d\n",ansi,ansj);
        }
    }
    return 0;
}
View Code

 

bzoj 2152

题:https://www.lydsy.com/JudgeOnline/problem.php?id=2152

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int M=2e4+4;
struct node{
    int v,nextt;
    int w;
}e[M<<1];
int head[M],dis[4],sz[M],maxv[M],vis[M],n,tot,fenzi,maxx,root;
void addedge(int u,int v,int w){
    e[tot].v=v;
    e[tot].nextt=head[u];
    e[tot].w=w;
    head[u]=tot++;
}
void dfssz(int u,int f){
    maxv[u]=0;
    sz[u]=1;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v==f||vis[v])
            continue;
        dfssz(v,u);
        sz[u]+=sz[v];
        maxv[u]=max(maxv[u],sz[v]);
    }
}
void dfsroot(int r,int u,int f){
    maxv[u]=max(maxv[u],sz[r]-sz[u]);
    if(maxx>maxv[u]){
        maxx=maxv[u];
        root=u;
    }
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v==f||vis[v])
            continue;
        dfsroot(r,v,u);
    }
}
void dfsdis(int u,int f,int d){
    dis[d%3]++;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(vis[v]||v==f)
            continue;
        dfsdis(v,u,(d+e[i].w)%3);
    }
}
ll cal(int u,int d){
    dis[0]=dis[1]=dis[2]=0;
    dfsdis(u,-1,d);
    ll sum=0;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            if((i+j)%3==0)
                sum+=dis[i]*dis[j];
    return sum;     
}
void solve(int u){
    maxx=n;
    dfssz(u,-1);
    dfsroot(u,u,-1);
    fenzi+=cal(root,0);
    vis[root]=1;
    for(int i=head[root];~i;i=e[i].nextt){
        int v=e[i].v;
        if(vis[v])
            continue;
        fenzi-=cal(v,e[i].w);
        solve(v);
    }
}
int main(){
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
        addedge(v,u,w);
    }
    solve(1);
    int fenmu=n*n;
    //cout<<fenmu<<endl;
    int g=__gcd(fenzi,fenmu);
    printf("%d/%d",fenzi/g,fenmu/g);
    return 0;
}
View Code

 题:https://ac.nowcoder.com/acm/contest/1099/I

题意:找出路径和为2019倍数的路径

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=1e18;
ll ans=0;
int tot,maxx,root;
const int M=2e4+5;
struct node{
    int v,w,nextt;
}e[M<<1];
int head[M],vis[M],sz[M],maxv[M],dis[2020],n;
void addedge(int u,int v,int w){
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nextt=head[u];
    head[u]=tot++;
}
void dfssz(int u,int f){
    sz[u]=1;
    maxv[u]=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;//cout<<"!!"<<endl;
        if(v==f||vis[v])
            continue;
        dfssz(v,u);
        sz[u]+=sz[v];
        maxv[u]=max(maxv[u],sz[v]);
    }
}
void dfsroot(int r,int u,int f){
    maxv[u]=max(maxv[u],sz[r]-sz[u]);
    if(maxv[u]<maxx){
        maxx=maxv[u];
        root=u;
    }
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(vis[v]||v==f)
            continue;
        dfsroot(r,v,u);
    }
}
void dfsdis(int u,int d,int f){
    dis[d%2019]++;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v==f||vis[v])
            continue;
        dfsdis(v,(d+e[i].w)%2019,u);
    }
}
ll cal(int u,int d){
    ll sum=0;
    for(int i=0;i<=2019;i++)
        dis[i]=0;
    dfsdis(u,d,-1);
    for(int i=0;i<2019;i++){
        if(dis[i]==0||dis[(2019-i)%2019]==0)
            continue;
        sum+=1ll*dis[i]*(dis[(2019-i)%2019]);
    }
   // mp.clear();
    return sum;
}
void solve(int u){
    maxx=n;
    dfssz(u,-1);
    dfsroot(u,u,-1);
    ans+=cal(root,0);
    
    vis[root]=1;
    for(int i=head[root];~i;i=e[i].nextt){
        int v=e[i].v;
        if(vis[v])
            continue;
        ans-=cal(v,e[i].w);
        solve(v);
    }
}
int main(){
    while(~scanf("%d",&n)){
        tot=0,ans=0ll;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<n;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        
        solve(1);
        printf("%lld\n",(ans-n)/2);//要减去自己到自己的贡献 
    }
    return 0;
}
View Code

 点分治好题:https://www.luogu.org/problem/P2664

最终思考于:https://blog.csdn.net/wu_tongtong/article/details/78963542

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int M=2e5+5;
int reco,total;
ll sz[M];
int head[M],C[M],vis[M],cnt[M],maxv[M],n,tot,root,num,sum,tt;
ll ans[M],col[M];
struct node{
    int v,nextt;
}e[M<<1];
void addedge(int u,int v){
    e[tot].v=v;
    e[tot].nextt=head[u];
    head[u]=tot++;
}
void dfsroot(int u,int f){
    sz[u]=1;
    ll maxson=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            dfsroot(v,u);
            sz[u]+=sz[v];
            maxson=max(maxson,sz[v]);
        }
    }
    maxson=max(maxson,total-sz[u]);
    if(maxson<reco)
        root=u,reco=maxson;
}
void dfs1(int u,int f){
    sz[u]=1;
    cnt[C[u]]++;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            dfs1(v,u);
            sz[u]+=sz[v];
        }
    }
    if(cnt[C[u]]==1){
        sum+=sz[u];
        col[C[u]]+=sz[u];
    }
    cnt[C[u]]--;
    
}
void change(int u,int f,int sign){
    cnt[C[u]]++;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v])
            change(v,u,sign);
    }
    if(cnt[C[u]]==1){
        sum+=1ll*sign*sz[u];
        col[C[u]]+=1ll*sign*sz[u];
    }
    cnt[C[u]]--;
}
void dfs2(int u,int f){
    cnt[C[u]]++;
    if(cnt[C[u]]==1){
        sum-=col[C[u]];
        num++;
    }
    ans[u]+=1ll*sum+num*tt;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            dfs2(v,u);
        }
    }
    if(cnt[C[u]]==1){
        sum+=col[C[u]];
        num--;
    }
    cnt[C[u]]--;
}
void cle(int  u,int f){
    cnt[C[u]]=0;
    col[C[u]]=0;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v])
            cle(v,u);    
    }
}
void doit(int u,int f){
    dfs1(u,f);
    ans[u]+=(ll)sum;
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            cnt[C[u]]++;
            col[C[u]]-=sz[v];
            sum-=sz[v];
            change(v,u,-1);
            cnt[C[u]]--;
            
            tt=sz[u]-sz[v];
            dfs2(v,u);
            
            cnt[C[u]]++;
            col[C[u]]+=sz[v];
            sum+=sz[v];
            change(v,u,1);
            cnt[C[u]]--;
        }
    }
    sum=num=0;
    cle(u,f);
}
void solve(int u,int f){
    
    vis[u]=1;
    doit(u,f);
    for(int i=head[u];~i;i=e[i].nextt){
        int v=e[i].v;
        if(v!=f&&!vis[v]){
            total=sz[v];
            reco=inf;
            dfsroot(v,u);
            solve(root,0);
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<=n;i++)
        head[i]=-1;
    for(int i=1;i<=n;i++)
        scanf("%d",&C[i]);
    for(int u,v,i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    total=n;
    reco=inf;
    dfsroot(1,0);
    solve(root,0);
    for(int i=1;i<=n;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
View Code

 

posted @ 2019-09-21 13:56  starve_to_death  阅读(157)  评论(0编辑  收藏  举报