给你一个n个点m条边无向图,每一个点有两个权a,b,现在要你选出恰好k个点,其中这k个点可以形成恰好1个联通块,对于每一个这样的选取方案,代价是max{a}+max{b},问最小的代价
其中n<=3e5,m<=5e5
大概的思路就是把每一条边都赋一个边权,把问题转化成选k-1条边,然后就可以lct(快速的求子树的大小)了
需要注意的是lct中不应该连的边不要乱连,没有的边不要乱删,就是因为这个东西调了超级久
以后做lct WA时应该要好好注意这两个东西
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int maxn=5e5+5;
struct P{
int x,y,t1,t2,id;
}a[maxn],s[maxn],q[maxn],g[maxn];
int son[maxn*2][3],fa[maxn*2],st[maxn*2],size[maxn*2],mi[maxn*2];
int xu[maxn*2];
int i,j,k,l,m,n,x,y,z,nt,la,ans,tp;
bool bz[maxn*2],bt[maxn*2],hv[maxn*2];
int cmp1(P x,P y){
return x.t1<y.t1;
}
int cmp2(P x,P y){
return x.t2<y.t2;
}
int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){
x=x*10+ch-48;
ch=getchar();
}
return x;
}
//LCT
bool isroot(int x){
if (son[fa[x]][0]==x || son[fa[x]][1]==x) return false;
return true;
}
void remark(int x){
if (bz[x]==true){
bz[x]=false;
int z=son[x][0]; son[x][0]=son[x][1]; son[x][1]=z;
bz[son[x][0]]^=1; bz[son[x][1]]^=1;
}
}
int update(int x){
size[x]=size[son[x][0]]+size[son[x][1]]+xu[x];
if (x>n){
if (g[mi[son[x][0]]].t2>g[x-n].t2) mi[x]=mi[son[x][0]]; else
mi[x]=x-n;
} else mi[x]=mi[son[x][0]];
if (g[mi[son[x][1]]].t2>g[mi[x]].t2) mi[x]=mi[son[x][1]];
}
void rotate(int x,int w){
int y=fa[x];
son[y][2-w]=son[x][w-1];
if (son[x][w-1]) fa[son[x][w-1]]=y;
fa[x]=fa[y];
if (!isroot(y)){
if (y==son[fa[y]][0]) son[fa[y]][0]=x; else son[fa[y]][1]=x;
}
fa[y]=x;
son[x][w-1]=y;
update(y); update(x);
}
void splay(int x){
int i,y;
tp=0;
while (!isroot(x)){
st[++tp]=x; x=fa[x];
}
st[++tp]=x;
fo1(i,tp,1) remark(st[i]);
x=st[1];
while (!isroot(x)){
y=fa[x];
if (isroot(y)){
if (x==son[y][0]) rotate(x,2); else rotate(x,1);
} else{
if (y==son[fa[y]][0]){
if (x==son[y][0]){
rotate(y,2); rotate(x,2);
} else{
rotate(x,1); rotate(x,2);
}
} else{
if (x==son[y][1]){
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); xu[x]=xu[x]+size[son[x][1]]-size[t];
son[x][1]=t; update(x); t=x;
}
}
void makeroot(int x){
access(x);
splay(x); bz[x]^=1;
}
void link(int x,int y){
makeroot(x);// makeroot(y);
fa[x]=y; xu[y]=xu[y]+size[x]; update(y);
}
void cut(int x,int y){
makeroot(x); access(y); splay(y); son[y][0]=0; fa[x]=0;
update(y);
}
bool tong(int x,int y){
makeroot(x); access(y); splay(y);
if (isroot(x)) return false; return true;
}
bool pan(int x){
makeroot(x);
if (size[x]return false; return true;
}
void Link(int x){
if (pan(g[x].x)) nt--;
if (pan(g[x].y)) nt--;
link(g[x].x,x+n);
link(g[x].y,x+n);
if (pan(x+n)) nt++;
}
void Cut(int x){
if (pan(x+n)) nt--;
cut(g[x].x,x+n);
cut(g[x].y,x+n);
if (pan(g[x].x)) nt++;
if (pan(g[x].y)) nt++;
}
int find(int x,int y){
makeroot(x); access(y); splay(y);
return mi[y];
}
//
void insert(int i){
bt[s[i].id]=true;
if (hv[s[i].id]) return;
if (tong(s[i].x,s[i].y)){
z=find(s[i].x,s[i].y);
if (s[i].t2s[i].id);
} else bt[s[i].id]=false;
} else
Link(s[i].id);
}
void go_del(){
while (true){
if (bt[q[la].id]==false){
hv[q[la].id]=true; la--; continue;
}
Cut(q[la].id);
if (!nt){
Link(q[la].id); break;
}
hv[q[la].id]=true;
la--;
}
ans=min(ans,s[i].t1+q[la].t2);
}
void prepare(){
fo(i,1,m){
xu[i+n]=1; mi[i+n]=i;
}
i=0;
while (nt==0 && i<m){
i++;
insert(i);
}
if (nt==0){
printf("no solution"); return;
}
la=m;
go_del();
i++;
}
int main(){
freopen("mincost.in","r",stdin);
freopen("mincost.out","w",stdout);
n=read(); m=read(); k=read();
fo(i,1,n){
a[i].x=read(); a[i].y=read(); a[i].id=i;
}
if (k==1){
ans=2e9;
fo(i,1,n) ans=min(ans,a[i].x+a[i].y);
printf("%d\n",ans);
return 0;
}
k--;
fo(i,1,m){
s[i].x=read(); s[i].y=read(); s[i].id=i;
s[i].t1=max(a[s[i].x].x,a[s[i].y].x);
s[i].t2=max(a[s[i].x].y,a[s[i].y].y);
g[i]=s[i];
q[i]=s[i];
}
sort(s+1,s+m+1,cmp1);
sort(q+1,q+m+1,cmp2);
ans=2e9;
prepare();
if (!nt) return 0;
for(;i<=m;i++){
insert(i);
go_del();
}
printf("%d\n",ans);
return 0;
}