您的位置:首页 > 其它

[jzoj 4807]. 【NOIP2016提高A组五校联考3】破解

2016-10-14 20:20 375 查看


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

The Solution

首先做差分。

每一位上的数变为和前面的那个数的异或值。

做完差分后的数组每一种不同的情况都对应着原串的每一种不同的情况。

即(u,v)这条边取0/1都是一种可能方案,我们新联通块的方案数就是原来的*2。

所以大小为n的方案数最终答案为2^n.

然后每个区间对应的操作只会改变查分数组的两个位置,分别为Li和Ri+1。

对于每个区间,我们把Li和Ri+1连一条无向边。

如果他们处于不同一个联通块,那么这个区间选择的方案及是与之前的方案不同,就把之前的翻倍。

再假如现在连了(u,v),而且 u,v 属于同一联通块,那么不管这条

边取 0 或取 1,我都可以通过翻转其他的边表示出新的方案,所以方案数不变。

连边操作以及联通块的维护可以用并查集。

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define M 100005
#define Maxn 10000005
#define mo 1000000007

using namespace std;

int n,m,tot = 0,Dad[Maxn];

struct note
{
int l,r;
}a[M];

int get(int x) {return Dad[x]==x?x:Dad[x]=get(Dad[x]);}

int main()
{
int T;
scanf("%d",&T);
while (T--)
{
tot = 0;
int p = 0;
int ans = 1;
scanf("%d%d",&n,&m);
fo(i,1,m) scanf("%d%d",&a[i].l,&a[i].r);
fo(i,1,m) Dad[a[i].l] = a[i].l ,Dad[(a[i].r)+1] = a[i].r+1;
fo(i,1,m)
{
int x= a[i].l,y=a[i].r;
if (get(x)!=get(y+1) || Dad[x]*Dad[y+1]==0)
{
Dad[Dad[x]] = Dad[y+1];
ans = (ans * 2) % mo;
}
}
fo(i,1,p) Dad[a[i].l] = 0 ,Dad[(a[i].r)+1] = 0;
printf("%d\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  并查集 联通块