[bzoj 3563&3569]DZY Loves Chinese I&II

27 篇文章 0 订阅

3263是道sb题,随便水过去吧。。。。。

3569才是正真的神题!

用dfs搞出一棵生成树,把边分为树边非树边

对于非树边我们随机分配一个数值给他,而树边上的值为覆盖其所有非树边权值的异或和。

我们来看一看删边的情况:

1、仅删除非树边,很明显这样一定仍是联通的。

2、删除树边和非树边,只有将树边和覆盖这条树边的非树边全部删除才能使图不联通,由于我们刚刚的预处理,这些边权值的异或和为0

3、仅删除树边,这种情况比较复杂,但是我们讨论一下只删除两条树边的情况可以发现也只有删除的边权异或和为0时才会使图不联通。

有了这些我们就能把原问题转化为求删除的边集是否有存在一个子集其权值异或和为0。

rhl大神介绍了一种特别屌的方法:开一个64位的数组,初始为0。每一权值x从63位向0位扫描,如果此为x为1且数组内无值,那么把此为赋值为x,退出。如果x此位为1且数组内有值,那么x异或上这个值。如果某个边值最终能够扫描完整数组就说明存在我们要找的子集,图不联通。(证明似乎和线性相关与线性无关有关)

为了减少对第一种情况的误判,最好将权值设为一个64位unsigned long long,自己也可以随便(生日)设一个随机种子防止被卡。

//千万别作死开srand(time(0)),八中上re了三次。。。。。

#include <ctime>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn=100005;
const int Maxm=1000005;
typedef unsigned long long ULL;
ULL g[Maxm],d[70],w[Maxm],tmp;
int node[Maxm],next[Maxm],a[Maxm],fa[Maxm],v[Maxm];
int n,m,s,cnt,i,x,y,q,tot,tim;

void Rand(ULL &x)
{ x = 1; for (int i=0;i<4;i++) x *= rand(); }

void add(int x,int y){
  node[++tot]=y; next[tot]=a[x]; a[x]=tot;
  node[++tot]=x; next[tot]=a[y]; a[y]=tot;
}

void dfs(int x){
  v[x] = ++tim;
  for (int i=a[x];i;i=next[i])
  if (fa[x]!=node[i]){
  	if (v[node[i]]!=0){
  	  if (v[node[i]]>v[x]) continue;
  	  Rand( w[i/2] );
  	  g[x] ^= w[i/2];
  	  g[node[i]] ^= w[i/2];
  	} else
  	{
  	  fa[node[i]] = x;
  	  dfs(node[i]);
  	  w[i/2] = g[node[i]];
  	  g[x] ^= g[node[i]];
  	}
  }
}

bool Judge(){
  for (int i=2;i<=n;i++)
    if (fa[i]==0) return 0;
  return 1;
}

int main(){
  scanf("%d%d",&n,&m);
  for (i=1,tot=1;i<=m;i++){
  	scanf("%d%d",&x,&y);
  	add(x,y);
  }
  dfs(1);
  if (!Judge()){
  	scanf("%d",&q);
  	while (q--)
  	  puts("Disconnected");
  	return 0;
  }
  cnt = 0;
  scanf("%d",&q);
  while (q--){
  	scanf("%d",&s);
  	bool flag = 0;
  	for (i=0;i<64;i++) d[i]=0;
  	while (s--){
  	  scanf("%d",&x);
	  x ^= cnt; tmp = w[x];
  	  for (i=63;i>=0;i--)
  	  if ( (tmp>>i)&1 ){
  	    if (d[i]!=0) tmp ^= d[i];
  	      else {d[i]=tmp;break;}
  	  }
  	  if (tmp==0) flag=1;
    }
    if (!flag) puts("Connected"), cnt++;
      else puts("Disconnected");
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值