5433. 【NOIP2017提高A组集训10.28】图

题目描述

Description

有一个n个点A+B条边的无向连通图,有一变量x,每条边的权值都是一个关于x的简单多项式,其中有A条边的权值是k+x,另外B条边的权值是k-x,如果只保留权值形如k+x的边,那么这个图仍是一个连通图,如果只保留权值形如k-x的边,这个图也依然是一个连通图。
给出q组询问,每组询问给出x的值,问此时这个无向连通图的最小生成树权值是多少。

Input

第一行四个数n,A,B和q
接下来A行,每行三个整数u,v,k,表示u和v之间有一条权值为k+x的无向边
接下来B行,每行三个整数u,v,k,表示u和v之间有一条权值为k-x的无向边
接下来q行,每行一个整数v,问当x=v时图的最小生成树权值是多少

Output

输出共q行,每行一个数表示对应询问的答案

Sample Input

5 4 4 4
1 3 2
1 2 0
3 5 5
3 4 10
5 4 7
2 3 6
1 2 1000
3 4 1000
0
1
2
3

Sample Output

14
16
18
18

Data Constraint

对于30%的数据,1<=n,q<=1000,n-1<=A,B<=2000
对于另外20%的数据,所有权值形如k+x的边的k满足,0<=k<=10^8,所有权值形如k-x的边的k满足910^8<=k<=10^9,所有询问的v满足0<=v<=410^8
对于另外40%的数据,1<=n<=1000,1<=q<=100000,n-1<=A,B<=2000
对于100%的数据,1<=n,q<=100000 , n-1<=A,B<=200000, 0<=k<=10^9 , -10^9<=v<=10^9

题解

显然按照正边和负边分别搞,每次加上一条负边,把树上最大的正边删掉

LCT搞边权=把边建点

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

struct type{
    int x,y,s;
} a[200001],b[200001];
int a2[200001];
struct Type{
    int x,id;
} q[100001];
struct TYPE{
    double x;
    int s;
} c[200001];
int tr[500001][2];
int Tr[500001][2];
int TR[500001];
bool rev[500001];
bool bz[500001]; //is root
int K[500001];
int fa[500001];
int Fa[100001];
long long ans[100001];
int n,A,B,Q,i,j,k,l,len,N;
long long sum;

void look()
{
    int i;
    
    fo(i,1,len)
    cout<<"son:"<<tr[i][0]<<" "<<tr[i][1]<<" father:"<<fa[i]<<" isroot:"<<bz[i]<<" reverse:"<<rev[i]<<" max:"<<Tr[i][0]<<" "<<Tr[i][1]<<endl;
    cout<<endl;
}

bool cmp(type a,type b)
{
    return a.s<b.s;
}
bool Cmp(Type a,Type b)
{
    return a.x<b.x;
}
bool CMP(TYPE a,TYPE b)
{
    return a.x<b.x;
}

int gf(int t)
{
    if (Fa[t]==t) return t;
    
    Fa[t]=gf(Fa[t]);
    return Fa[t];
}

void swap(int &x,int &y)
{
    int z=x;
    x=y;
    y=z;
}

void up(int t)
{
    Tr[t][0]=a[TR[t]].s;
    Tr[t][1]=TR[t];
    
    if (tr[t][0] && Tr[tr[t][0]][0]>Tr[t][0])
    {
        Tr[t][0]=Tr[tr[t][0]][0];
        Tr[t][1]=Tr[tr[t][0]][1];
    }
    if (tr[t][1] && Tr[tr[t][1]][0]>Tr[t][0])
    {
        Tr[t][0]=Tr[tr[t][1]][0];
        Tr[t][1]=Tr[tr[t][1]][1];
    }
}

void down(int t)
{
    if (rev[t])
    {
        swap(tr[t][0],tr[t][1]);
        rev[t]=0;
        
        rev[tr[t][0]]^=1;
        rev[tr[t][1]]^=1;
    }
}

void rot(int t)
{
    down(fa[t]);
    down(t);
    
    int Fa=fa[t],Fa2=fa[Fa],x=tr[Fa][1]==t,x2=tr[Fa2][1]==Fa,son=tr[t][x^1];
    
    fa[son]=Fa;
    tr[Fa][x]=son;
    
    tr[t][x^1]=Fa;
    fa[Fa]=t;
    
    if (!bz[Fa])
    tr[Fa2][x2]=t;
    fa[t]=Fa2;
    
    bz[t]=bz[Fa];
    bz[Fa]=0;
    
    up(Fa);
    up(t);
}

void splay(int t)
{
    int Fa,Fa2;
    
    down(t); //
    
    while (!bz[t])
    {
        Fa=fa[t];
        
        if (!bz[Fa])
        {
            Fa2=fa[Fa];
            
            if (((tr[Fa2][0]==Fa)^(tr[Fa][0]==t))==0)
            rot(Fa),rot(t);
            else
            rot(t),rot(t);
        }
        else
        rot(t);
    }
}

void access(int t)
{
    int ls=0,x=t;
    
    while (t)
    {
        splay(t);
        
        bz[tr[t][1]]=1;
        bz[ls]=0;
        
        tr[t][1]=ls;
        up(t);
        
        ls=t;
        t=fa[t];
    }
    splay(x);
}

void moveroot(int t)
{
    access(t);
    rev[t]=1;
}

void link(int x,int y)
{
    moveroot(x);
    fa[x]=y;
}

void cut(int x,int y)
{
    moveroot(x);
    access(y);
    splay(x);
    
    tr[x][1]=0;
    bz[y]=1;
    fa[y]=0;
    
    up(x);
}

int main()
{
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    
    memset(bz,1,sizeof(bz));
    
    scanf("%d%d%d%d",&n,&A,&B,&Q);len=n;
    a[0].s=-2133333333;
    fo(i,1,A) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].s);
    fo(i,1,B) scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].s);
    fo(i,1,Q)
    {
        scanf("%d",&q[i].x);
        q[i].id=i;
    }
    
    sort(q+1,q+Q+1,Cmp);
    
    fo(i,1,n)
    {
        Tr[i][0]=-2133333333;
        Tr[i][1]=0;
        TR[i]=0;
        
        Fa[i]=i;
    }
    
    sort(a+1,a+A+1,cmp);
    sort(b+1,b+B+1,cmp);
    
    fo(i,1,A)
    if (gf(a[i].x)!=gf(a[i].y))
    {
        Fa[Fa[a[i].x]]=Fa[a[i].y];
        
        a2[i]=++len;
        Tr[len][0]=a[i].s;
        Tr[len][1]=i;
        TR[len]=i;
        
        link(len,a[i].x);
        link(len,a[i].y);
        
        sum+=a[i].s;
    }
    
    fo(i,1,n)
    Fa[i]=i;
    
    fo(i,1,B)
    if (gf(b[i].x)!=gf(b[i].y))
    {
        Fa[Fa[b[i].x]]=Fa[b[i].y];
        
        moveroot(b[i].x);
        access(b[i].y);
        
        if (Tr[b[i].y][1])
        {
            j=Tr[b[i].y][1];
            
            cut(a[j].x,a2[j]);
            cut(a[j].y,a2[j]);
            
            ++len;
            Tr[len][0]=-2133333333;
            Tr[len][1]=0;
            TR[len]=0;
            
            link(len,b[i].x);
            link(len,b[i].y);
            
            ++N;
            c[N].x=(b[i].s-a[j].s)/2.0;
            c[N].s=b[i].s-a[j].s;
        }
    }
    
    sort(c+1,c+N+1,CMP);
    
    j=0;
    fo(i,1,Q)
    {
        while (j<N && q[i].x>=c[j+1].x)
        ++j,sum+=c[j].s;
        
        ans[q[i].id]=sum+(long long)q[i].x*((n-1)-j-j);
    }
    
    fo(i,1,Q)
    printf("%lld\n",ans[i]);
    
    fclose(stdin);
    fclose(stdout);
    
    return 0;
}

转载于:https://www.cnblogs.com/gmh77/p/11579906.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值