4.18 省选模拟赛 桥 边双联通分量 长链剖分维护贪心
作者:互联网
只存在加边操作 所以每次只对割边有影响。
考虑求出所有的边双联通分量 然后进行缩点。
那么原图就变成了一颗树 且所有边都是割边。
考虑k==1的时候 显然是求出树的直径。
考虑k>1时 一个错误的贪心:把刚才树的直径上的边标记为0 然后再求直径......
容易构造出反例让其错误。
题解上的做法过于神仙 看不懂。
给一种自己做CF某道题后得到的贪心思路。
容易发现选出k条边 某种意义上来说是选出了2k个点。
我们还可以知道这2k个点中 其中必然有两个点包含树的直径两端点。
但是直接选出2k个点可能是不合法的。而且此时无根很难计算出来答案。
我们知道了必然有两个点是树的直径两端点了 以其中一个点为根。
那么剩下的就是选取2k-1个点每个点的最初权值为点到根路径上的边数。
考虑每次选取最大的。这个贪心容易使用长链剖分进行优化 排一下序或者使用堆维护即可。
正确性:最初根为直径某个端点显然正确,接着这2k个点包括根显然是可以两两配对组成k条边和题目吻合。
最优性:每次权值都尽可能的大所以是最优的。(还需要斟酌一下 好多题都是这样贪的
const int MAXN=200010;
int n,m,Q,id,len=1,cnt,cc,len1=1,s1,s2,maxx;
int c[MAXN],vis[MAXN],dis[MAXN];
int dfn[MAXN],low[MAXN],mark[MAXN<<1],f[MAXN],q[MAXN],mx[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],sz[MAXN],son[MAXN],fa1[MAXN];
int lin1[MAXN],ver1[MAXN<<1],nex1[MAXN<<1],e1[MAXN<<1];
inline void add(int x,int y)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
}
inline void add1(int x,int y,int z)
{
ver1[++len1]=y;
nex1[len1]=lin1[x];
lin1[x]=len1;
e1[len1]=z;
}
inline void dfs(int x,int las)
{
dfn[x]=low[x]=++cnt;
go(x)
{
if(i==(las^1))continue;
if(!dfn[tn])
{
dfs(tn,i);
low[x]=min(low[x],low[tn]);
if(low[tn]>dfn[x])mark[i]=1;
}
else low[x]=min(low[x],dfn[tn]);
}
}
inline void dfs(int x)
{
vis[x]=1;c[x]=id;
go(x)
{
if(vis[tn]||mark[i]||mark[i^1])continue;
dfs(tn);
}
}
inline int bfs(int w)
{
maxx=0;++cc;
int l=0,r=0,ww=0;
q[++r]=w;dis[w]=0;vis[w]=cc;
while(++l<=r)
{
int x=q[l];
if(dis[x]>maxx)
{
ww=x;
maxx=dis[x];
}
for(int i=lin1[x];i;i=nex1[i])
{
int tn=ver1[i];
if(vis[tn]!=cc)
{
dis[tn]=dis[x]+e1[i];
vis[tn]=cc;
q[++r]=tn;
}
}
}
return ww;
}
inline int dp(int x,int fa)
{
if(x==s2)return 1;
for(int i=lin1[x];i;i=nex1[i])
{
int tn=ver1[i];
if(tn==fa)continue;
if(dp(tn,x))
{
e1[i]=e1[i^1]=0;
return 1;
}
}
return 0;
}
inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
inline void dp1(int x,int fa)
{
sz[x]=sz[fa]+1;mx[x]=sz[x];
fa1[x]=fa;
for(int i=lin1[x];i;i=nex1[i])
{
int tn=ver1[i];
if(tn==fa)continue;
dp1(tn,x);
//cout<<mx[tn]<<' '<<mx[x]<<endl;
if(mx[tn]>mx[x])
{
son[x]=tn;
mx[x]=mx[tn];
}
// cout<<son[x]<<endl;
}
}
priority_queue<int>s;
inline void dp2(int x,int fa)
{
if(fa==x)
{
if(x==s1)s.push(mx[x]-sz[x]);
else s.push(mx[x]-sz[fa1[x]]);
//cout<<mx[x]-sz[x]<<endl;
//cout<<son[x]<<' '<<x<<endl;
}
if(son[x])dp2(son[x],fa);
for(int i=lin1[x];i;i=nex1[i])
{
int tn=ver1[i];
if(tn==fa1[x]||son[x]==tn)continue;
dp2(tn,tn);
//cout<<"ww"<<endl;
}
}
int main()
{
//freopen("1.in","r",stdin);
freopen("bridge.in","r",stdin);
freopen("bridge.out","w",stdout);
get(n);get(m);get(Q);
rep(1,m,i)
{
int get(x);int get(y);
add(x,y);add(y,x);
}
dfs(1,0);cc=1;
rep(1,n,i)if(!vis[i])++id,dfs(i);
rep(1,id,i)f[i]=i;
rep(1,n,j)go(j)
{
if(c[j]==c[tn])continue;
int xx=getfather(c[j]);
int yy=getfather(c[tn]);
if(xx==yy)continue;
f[xx]=yy;
add1(c[j],c[tn],1);
add1(c[tn],c[j],1);
}
--id;int ans=0;
s1=bfs(1);s2=bfs(s1);
dp1(s1,0);
dp2(s1,s1);
ans+=s.top();
s.pop();
put(ans);
rep(2,Q,i)
{
if(s.size())
{
ans+=s.top();
s.pop();
}
if(s.size())
{
ans+=s.top();
s.pop();
}
put(ans);
}
return 0;
}
标签:长链,边双,剖分,vis,int,tn,fa,MAXN,inline 来源: https://www.cnblogs.com/chdy/p/12770030.html