jzoj【NOIP2017提高A组集训10.28】图

题目

Time Limits: 2500 ms Memory Limits: 524288 KB Detailed Limits

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满足9*10^8<=k<=10^9,所有询问的v满足0<=v<=4*10^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

题解

这一道题一开始没有想的太清楚就照着题解打了,结果打完之后改了超级久,最后还是自己yy出了自己错误想法的一个反例才把做法更正过来。。。

首先有一个很显然的结论:每一个时刻的边都是由两类边的最小生成树中的边构成的(反证,如果有一条不是这样的边那么在连接这一条边之前两点对应的联通块之间必定有最小生成树中的边相连)

当x–>-∞时我们所有的边都是正边,x–>∞时都是负边,也就是说我们可以把询问排序,然后就变成了一个负边不断的代替正边的过程。

那么我们可以先把正图的最小生成树设为当前树,然后从小到大往里面加负最小生成树里面的边,这里有一点需要注意:不一定是边权小的负边先加到图里面(见样例)但是呢当两条负图中的边在新图之中的最大的边是同一条边是显然k值小的会去替换那条边

所以我们就可以这样子把每一个替换边的x值算出来,然后我们就可以计算答案了

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define ll long long

using namespace std;

const int maxn=1e6+5;

struct P{
    int x,y,z;
}a[maxn],b[maxn];
struct S{
    ll x,y,z;
}q[maxn],v[maxn];
int fa[maxn],father[maxn],son[maxn][3],key[maxn],mx[maxn];
int d[maxn];
bool bz[maxn];
int i,j,k,l,m,n,A,B,Q,x,y,o,js;
ll ans,t1,t2,s;
ll an[maxn];
char ch;

int read(){
    int x=0,y=1;
    ch=getchar();
    while (ch<'0' || ch>'9'){
        if (ch=='-') y=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9'){
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*y;
}
int cmp(P x,P y){
    return x.z<y.z;
}
int cmp1(S x,S y){
    return x.x<y.x;
}
int getfather(int x){
    if (father[x]==x) return x; else father[x]=getfather(father[x]);
    return father[x];
}
bool isroot(int x){
    if (son[fa[x]][1]!=x && son[fa[x]][2]!=x) return true; else return false;
}
void remark(int x){
    if (bz[x]==true){
        bz[x]=false;
        bz[son[x][1]]^=1;
        bz[son[x][2]]^=1;
        int t=son[x][1];
        son[x][1]=son[x][2]; son[x][2]=t;
    }
}
void update(int x){
    if (key[mx[son[x][1]]]>key[mx[son[x][2]]]) mx[x]=mx[son[x][1]]; else mx[x]=mx[son[x][2]];
    if (key[x]>key[mx[x]]) mx[x]=x;
}
void rotate(int x,int w){
    int y=fa[x];
    son[y][3-w]=son[x][w];
    if (son[x][w]) fa[son[x][w]]=y;
    if (!isroot(y)){
        if (son[fa[y]][1]==y) son[fa[y]][1]=x; else son[fa[y]][2]=x;
    }
    fa[x]=fa[y];
    fa[y]=x;
    son[x][w]=y;
    update(y); update(x);
}
void splay(int x){
    int i,y;
    d[0]=0;
    while (!isroot(x)){
        d[++d[0]]=x;
        x=fa[x];
    }
    d[++d[0]]=x;
    fo1(i,d[0],1) remark(d[i]);
    x=d[1];
    while (!isroot(x)){
        y=fa[x];
        if (isroot(y)){
            if (x==son[y][1]) rotate(x,2); else rotate(x,1);
        } else{
            if (son[fa[y]][1]==y){
                if (x==son[y][1]){
                    rotate(y,2); rotate(x,2);
                } else{
                    rotate(x,1); rotate(x,2);
                }
            } else{
                if (x==son[y][2]){
                    rotate(y,1); rotate(x,1);
                } else{
                    rotate(x,2); rotate(x,1);
                }
            }
        }
    }
}
void access(int x){
    for(int t=0;x;x=fa[x]) splay(x),son[x][2]=t,update(x),t=x;
}
void makeroot(int x){
    access(x); splay(x); bz[x]^=1;
}
void link(int x,int y){
    makeroot(x); fa[x]=y;
}
void cut(int x,int y){
    makeroot(x); access(y); splay(y); son[y][1]=0; fa[x]=0; update(y);
}
void geng(int x){
    cut(a[x].x,x+n); cut(a[x].y,x+n);
    link(b[i].x,i+n+A);
    link(b[i].y,i+n+A);
}
int find(int x,int y){
    makeroot(x); access(y); splay(y); 
//  if (key[mx[y]]==key[0]) return -1;
    return (mx[y]-n);
}
void Link(int x,int y){
    key[x+n]=y;
    mx[x+n]=x+n;
    link(a[x].x,x+n);
    link(a[x].y,x+n);
}
int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    n=read(); A=read(); B=read(); Q=read();
    fo(i,1,A){
        a[i].x=read(); a[i].y=read(); a[i].z=read();
    }
    fo(i,1,B){
        b[i].x=read(); b[i].y=read(); b[i].z=read();
    }
    sort(a+1,a+A+1,cmp);
    sort(b+1,b+B+1,cmp);
    fo(i,1,n) father[i]=i; m=0;
    memset(key,128,sizeof(key));
    fo(i,1,A){
        x=getfather(a[i].x); y=getfather(a[i].y);
        if (x==y) continue;
        m++;
        Link(i,a[i].z);
        father[x]=y;
        s=a[i].z; ans+=s;
        if (m==n-1) break;
    }
    m=0;
    fo(i,1,n) father[i]=i;
    fo(i,1,B){
        if (js==n-1) break;
        x=getfather(b[i].x); y=getfather(b[i].y);
        if (x==y) continue;
        father[x]=y; js++;
        o=find(b[i].x,b[i].y);
        v[++m].x=(b[i].z-a[o].z)/2;
        if (b[i].z-a[o].z>0 && ((b[i].z-a[o].z)%2)==1) v[m].x++;
        v[m].y=a[o].z;
        v[m].z=b[i].z;
        geng(o);
    }
    sort(v+1,v+m+1,cmp1);
    fo(i,1,Q){
        q[i].x=read(); q[i].y=i;
    }
    sort(q+1,q+Q+1,cmp1);
    m=0;
    fo(i,1,Q){
        while (q[i].x>=v[m+1].x && m<n-1){
            m++;
            ans=ans-v[m].y+v[m].z;
        }
        s=n-2*m-1;
        an[q[i].y]=ans+s*q[i].x;
    }
    fo(i,1,Q) printf("%lld\n",an[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值