[思维][DFS]JZOJ 4807 破解

Description

 

Input

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

Output

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

Sample Input

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

Sample Output

8
4
 

Data Constraint

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

Hint

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

分析

有个显然的结论,操作完的串必定是000...111...000...111这样的串

那么我们只需要知道01交界的位置即可,因为位置不同,则串必定不同

显然,这个位置一定是区间$[l,r)$的端点,而且操作偶数次时,这个端点必不是关键位置

那我们可以从$l$向$r$连边,这会构成许多联通块,不难想到联通块中的都是关键点,所以一个联通块的方案数为$2^{n-1}$,n为联通块中点的个数

那么有$m$个点,$k$个联通块,答案即为$2^{m-k}$

 

#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;
const int N=1e5+10;
const int P=1e9+7;
struct Graph {
    int v,nx;
}g[2*N];
int cnt,list[100*N];
bool vis[100*N];
int t,n,m,p;

void Add(int u,int v) {
    g[++cnt]=(Graph){v,list[u]};list[u]=cnt;
    g[++cnt]=(Graph){u,list[v]};list[v]=cnt;
}

void DFS(int u) {
    p++;vis[u]=1;
    for (int i=list[u];i;i=g[i].nx)
        if (!vis[g[i].v]) DFS(g[i].v);
}

int Power(int x,int y) {
    int ans=1;
    while (y) {
        if (y&1) ans=1ll*ans*x%P;
        x=1ll*x*x%P;
        y>>=1;
    }
    return ans;
}

int main() {
    for (scanf("%d",&t);t;t--) {
        int k=0;p=0;
        scanf("%d%d",&n,&m);
        cnt=0;memset(list,0,sizeof list);memset(vis,0,sizeof vis);
        for (int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),Add(u,v+1);
        for (int i=1;i<=n;i++)
            if (!vis[i]&&list[i]) DFS(i),k++;
        printf("%d\n",Power(2,p-k));
    }
}
View Code

 

posted @ 2019-07-01 11:15  Vagari  阅读(130)  评论(0编辑  收藏  举报