5433. 【NOIP2017提高A组集训10.28】图
题目
题目大意
给你一个无向连通图,有两种边,为\(k-x\)和\(k+x\)的形式。
有一堆询问,问\(x\)为某个值的时候的最小生成树。
思考历程
有过一些猜想,但被自己推翻了。
没有想出来,于是只能打暴力。
正解
首先有个比赛时就想到的一个很显然的结论:
肯定是正边和负边分别做一次最小生成树,然后用这些树边来生成新的最小生成树。
当时我是这么想的:可以先对正边做一次最小生成树,然后一条一条负边加进去,可以用\(LCT\)维护。最终形成的一定是最优的。所以只有正边的最小生成树的边有意义。反过来,只有负边的最小生成树的边有意义。
于是就只有两棵树的边有意义。
所以就先做一遍最小生成树。然后对正边建立\(LCT\)。
将负边排序,一个一个加进去。每次替换路径上权值最大的正边,并且记录替换的时候的\(x\)的下界。
搞完之后将\(x\)的下界排个序,就可以得到各个时段的最小生成树。
为什么是正确的呢?
如果有两条边,它们端点覆盖的路径的最大边是相同的(在没有进行任何加边操作和删边操作的时候)。很显然,更小的那个会优先替代(时间上也是它更小),而更大的那个会替代路径上其它的比较大的边。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define M 200010
#define INF 2000000000
#define ll long long
int n,A,B;
struct edge{
int u,v,len;
} ea[M],eb[M];
bool cmpe(const edge &a,const edge &b){return a.len<b.len;}
int ufs[N];
int getufs(int x){return ufs[x]==x?x:ufs[x]=getufs(ufs[x]);}
inline void init(edge *e,int &m){
for (int i=1;i<=m;++i)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].len);
sort(e+1,e+m+1,cmpe);
int tmp=0;
for (int i=1;i<=n;++i)
ufs[i]=i;
for (int i=1;i<=m;++i){
int x=getufs(e[i].u),y=getufs(e[i].v);
if (x!=y){
ufs[x]=y;
e[++tmp]=e[i];
}
}
m=tmp;
}
struct Node *null;
struct Node{
Node *fa,*c[2];
bool rev,isr;
int len;
Node *mx;
inline void rvs(){swap(c[0],c[1]),rev^=1;}
inline void push(){if (!isr) fa->push();if (rev) rev=0,c[0]->rvs(),c[1]->rvs();}
inline bool getson(){return fa->c[0]!=this;}
inline void upd(){mx=(c[0]->mx->len>c[1]->mx->len?c[0]->mx:c[1]->mx),mx=(mx->len<len?this:mx);}
inline void rotate(){
Node *y=fa;
if (y->isr)
y->isr=0,isr=1;
else
y->fa->c[y->getson()]=this;
bool k=getson();
fa=y->fa;
y->c[k]=c[!k],c[!k]->fa=y;
c[!k]=y,y->fa=this;
mx=y->mx,y->upd();
}
inline void splay(){
push();
for (;!isr;rotate())
if (!fa->isr)
getson()!=fa->getson()?rotate():fa->rotate();
}
inline Node *access(){
Node *x=this,*y=null;
for (;x!=null;y=x,x=x->fa){
x->splay();
x->c[1]->isr=1;
y->isr=0;
x->c[1]=y;
x->upd();
}
return y;
}
inline Node *mroot(){
Node *res=access();
res->rvs();
return res;
}
inline void link(Node *y){y->mroot()->fa=this;}
} d[N],eda[N],edb[N];
int tim[N],con[N],p[N];
bool cmpp(int a,int b){return tim[a]<tim[b];}
struct Query{
int x,num;
} q[N];
bool cmpq(Query a,Query b){return a.x<b.x;}
ll ans[N];
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
int Q;
scanf("%d%d%d%d",&n,&A,&B,&Q);
init(ea,A),init(eb,B);
null=new Node;
*null={null,null,null,0,0,-INF,null};
for (int i=1;i<=n;++i)
d[i]={null,null,null,0,1,-INF,null};
for (int i=1;i<=A;++i){
eda[i]={null,null,null,0,1,ea[i].len,&eda[i]};
d[ea[i].u].link(&eda[i]);
d[ea[i].v].link(&eda[i]);
}
ll sum=0;
for (int i=1;i<=A;++i)
sum+=ea[i].len;
tim[0]=-INF;
con[0]=0;
for (int i=1;i<=B;++i){
p[i]=i;
int u=eb[i].u,v=eb[i].v;
d[u].mroot();
Node *t=d[v].access();
if (t->mx->len==-INF){
tim[i]=INF;
continue;
}
tim[i]=(eb[i].len-t->mx->len-1>>1)+1;
con[i]=eb[i].len-t->mx->len;
t=t->mx;
d[ea[t-eda].u].mroot();
d[ea[t-eda].v].access();
t->splay();
t->c[0]->isr=t->c[1]->isr=1;
t->c[0]->fa=t->c[1]->fa=null;
edb[i]={null,null,null,0,1,-INF,null};
edb[i].link(&d[u]);
edb[i].link(&d[v]);
}
sort(p+1,p+B+1,cmpp);
for (int i=1;i<=Q;++i)
scanf("%d",&q[i].x),q[i].num=i;
sort(q+1,q+Q+1,cmpq);
for (int i=1,j=1,k=n-1;i<=Q;++i){
for (;j<=B && tim[p[j]]<=q[i].x;++j){
sum+=con[p[j]];
k-=2;
}
ans[q[i].num]=sum+(ll)k*q[i].x;
}
for (int i=1;i<=Q;++i)
printf("%lld\n",ans[i]);
return 0;
}
总结
有了结论,一切都好说……
我一开始就应该猜结论,拍验证……