zoukankan      html  css  js  c++  java
  • Luogu P1197 [JSOI2008]星球大战

    题外话:

    说真的这是一道不写篇博客都对不起我半天debug的时间

    真的顺手太可怕了


    正着删点然后每次判联通的复杂度还是很高的,显然不是这么做的

    这个时候我们需要逆向思维(√

    考虑从最终状态出发,将删点变成增加点,好像复杂度要低了那么一点点(大概是一大点点吧

    利用一个标记数组判断当前点是否还没有被加入图中,首先利用并查集计算最终状态的连通块个数,

    然后倒序向图中加点:

    首先先把某个点扔进图中,ans++;

    然后对这个点的所有边进行一波操作,判断是不是会和其他的连通块一起(并查集),变成大联通块(ans--)

    最后倒序输出就好了

    是离线做法?

    CODE:

    #include<bits/stdc++.h>
    
    using namespace std;
    
    inline int read(){
    	int ans=0;
    	char last=' ',ch=getchar();
    	while(ch>'9'||ch<'0') last=ch,ch=getchar();
    	while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    	if(last=='-') ans=-ans;
    	return ans;
    }
    
    const int mxn=400010;
    
    int n,m,k;
    int ans,p[mxn],P,fa[mxn];
    bool bj[mxn];
    int K[mxn];
    struct node {
    	int to,nxt;
    }e[mxn<<1];
    int head[mxn],ecnt;
    void add(int from,int to) {
    	++ecnt;
    	e[ecnt].nxt=head[from];
    	head[from]=ecnt;
    	e[ecnt].to=to;
    }
    
    int find(int x) {
    	if(fa[x]!=x) 
    		fa[x]=find(fa[x]);
    	return fa[x];
    }
    
    int main() {
    	n=read();
    	m=read();
    	for(int i=1,x,y;i<=m;i++) {
    		x=read();
    		x++;
    		y=read();
    		y++;
    		add(x,y);
    		add(y,x);
    	}
    	k=read();
    	for(int i=1,a;i<=k;i++) {
    		a=read();
    		K[i]=++a;
    		bj[K[i]]=1;
    	}
    	for(int i=1;i<=n;i++) fa[i]=i;
    	for(int i=1;i<=n;i++) {
    		if(bj[i]) continue;
    		for(int j=head[i],v;j;j=e[j].nxt) {
    			v=e[j].to;
    			if(bj[v]) continue;
    			int u=find(v);
    			int I=find(i);
    			if(u!=I)
    				fa[u]=I;
    		}
    	}
    	for(int i=1;i<=n;i++) 
    		if(bj[i]==0&&fa[i]==i) ans++;
    	p[++P]=ans;
    	for(int i=k;i>=1;i--) {
    		bj[K[i]]=0;ans++;
    		for(int j=head[K[i]],v;j;j=e[j].nxt) {
    			v=e[j].to;
    			if(bj[v]==1) continue;
    			int u=find(v);
    			int z=find(K[i]);
    			if(u!=z) {
    				ans--;
    				fa[u]=z;
    			}
    		}
    		p[++P]=ans;
    	}
    	for(int i=P;i>=1;i--) 
    		printf("%d
    ",p[i]);
    	return 0;
    }
    

    debug日志:

    1.把K[]开成了bool数组,还以为自己读入读炸了

    2.思路bug,傻不愣登的以为将K[i]点和某个连通块合并以后,如果又有一个属于这个连通块的点与K[i]有边,会影响答案。然后考虑丢点的时候,直接用head[K[i]]!=0判断是不是又自成一连通块了,忘记它有的点还没加进来。

    3.顺手什么的太恶心了。习惯于

    for(int i=head[u],v;i;i=e[i].nxt)
        v=e[i].to;
    

    的写法,于是就写成了:

    for(int j=head[u],v;j;;j=e[i].nxt)
        v=e[i].to;
    
  • 相关阅读:
    dnn重置Host密码
    fiddle 网址过滤
    生成下面的模块时,启用了优化或没有调试信息
    关闭web.config的继承
    jQuery最佳实践
    坐标高速插入,移动和查询算法
    索引缓存方面的一些测试数据
    转帖微軟将从 .NET 4 以后的版本弃用 System.Data.OracleClient
    算法系列计数排序
    一款免费生成流程图的插件
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/11812384.html
Copyright © 2011-2022 走看看