【JZOJ4807】破解

Description

历经千辛万苦,ddddddpppppp 终于找到了IBN5100。 dp 事先了解到SERN 共有T 个密码,每个密码是一个长度为N 的01 串,他要利用IBN5100 的特殊功能破解SERN 的密码。 初始时,IBN5100 中的串每个位置都是0。 这台特殊的IBN5100 还提供了M 个区间[Li,Ri],每次操作是从给定区间中选 择其中一个区间[Li,Ri],将当前01 串[Li,Ri]的位置上的数字全部取反。 dp 可以执行上述操作任意次。为了破解出密码,dp 想知道这个01 串最多有多 少种可能。 
由于答案可能很大,所以你只需要告诉 dp 模(10^9+7)后的答案即可。 
注意:每次破解都是独立的。

Input

第一行,一个整数T 表示一共T 组数据。 
每组数据第一行,两个整数N,M,分别表示密码串长度和区间个数。 
接下来M 行,第i 行两个整数Li,Ri 表示一个区间[Li,Ri]。

Output

每组数据一行,一个整数表示所有的可能,答案对(10^9+7)取模。

Sample Input


3 3 
1 1 
2 2 
3 3 
5 2 
1 2 
4 5

Sample Output


4

Hint

【样例解释】 
第一组数据:每个位置都可以单个修改,所以所有长度为3 的01 串都有可能,即2^3=8 种可能。 
第二组数据的四种可能如下: 
1.不操作:00000 
2.选择区间[1,2]:11000 
3.选择区间[4,5]:00011 
4.先选择区间[1,2]再选择[4,5]:11011

【数据范围】 
对于30%的数据,N,M ≤ 10 
对于60%的数据,N ≤ 10000000 , M ≤ 20 
对于100%的数据,N ≤ 10000000 , M ≤ 100000 ,1 ≤ Li ≤ Ri ≤ N , T≤ 10

 
题解:
     这个题目很巧秒,显然如果区间与区间之间没有相互之间影响,那么答案就会是2^n次方,也就是每个区间有乘二的贡献,因为既然你相互不影响,那我这个区间就有反和不反两种情况,但既然会影响所以我们要想办法把本质不同的组合给筛选出来。
  就是一个区间或者一个组合如果能被多个区间或者组合所取代,那么他们的本质就是一样的,所以我们只要乘一次二就可了,怎么筛选,用并查集连边判重就可以了。
 
代码:
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<cstring>
#define ll long long
#define mod 1000000007;
const int MAXN=10000010;
using namespace std;
int fa[MAXN],l[MAXN],r[MAXN],v[MAXN];
int n,m,t;
int num=0;
 
int find(int x){
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
 
void cl(){
    memset(fa,0,sizeof(fa));
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    memset(v,0,sizeof(v));
}
 
int main(){
    scanf("%d",&t);
    while(t--){
        cl();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&l[i],&r[i]);r[i]++;
        }
        for(int i=1;i<=n+3;i++) fa[i]=i;
        ll ans=1;
        for(int i=1;i<=m;i++){
            int x=find(l[i]),y=find(r[i]);
            if(x==y) continue;
            ans*=2;ans%=mod;
            fa[x]=y;
        }
        printf("%lld
",ans);
    }
}
…………
原文地址:https://www.cnblogs.com/renjianshige/p/7288081.html