2018.10.03队测总结
T3 克卜勒(kepler)<好题(毒瘤题)置顶>
一闪一闪亮晶晶/好像你的身体/藏在众多孤星之中/还是找得到你/挂在天上放光明/反射我的孤寂/提醒我/我也只是一颗寂寞的星星/oh~/浩瀚的世界里/更迭的人海里/和你互相辉映/而我们连续/连接所有思念/唱一首歌给你/给你
Description
Zyh在浩瀚的宇宙中发现了一个神奇的星系。这个星系上很可能有文明的痕迹,因为它的星球的连接方式非常特别。
具体的来说整个星系由若干个小星系组成,这若干个小星系是由若干个星球组成的。星系由n个小星系组成,这n个小星系在这里可以抽象成一个小圆。小星系编号1~n首尾相接形成一个大圆。若将第i个小星系放大,那么看到的就是Ci个星球。这些星球也是首尾相接形成了一个大圆。特别地,每个小星系中有两个星球x,y,分别成为起始星球和结束星球。在整个星系大圆的构成中实际上是由上一个小星系的结束星球连接着下一个小星系的起始星球。如果嫌弃语文拿B的Zyh说的不清楚可以看下面的图。
其中用黄绿色细线画的圈是一个个小星系,其中是有若干个星球,红色是起始星球,蓝色是结束星球。
然而星球之间的通信是一个问题。Zyh认为,星球会有两个状态,允许通行和禁止通行。星球的编号即使是在不同的小星系中也是两两不同的,如果是第I个小星系的第J个星球,那么编号应该是[sigma(1<=i<I)Ci]+J。如果星球p要去向星球q,那么如果存在一条路径,这条路径上的星球的状态都是允许通行,那么称这两个星球是可以到达的。然而复杂的是,这些星球都是会改变状态的。Zyh观察了m个改变操作和询问,他想要回答这些询问,于是他找到了你。
Input
首先是一个正整数n表示小星系的个数,然后是n行。每行的开始是Ci表示这个小星系的星球个数。接着是Ci+2个数字,分别是S[1] …… S[Ci] x y。S[j]表示第i个小星系的第j个星球的通行状态。0表示禁止通行,1表示允许通行。x和y表示这个小星系的第x第y个分别是起始和结束星球。
然后是一个正整数m,表示操作和询问总数。接下来是m行。如果是操作那么是这样的形式给出:1 x,表示将编号为x的星球状态置反。如果是询问:2 x y,表示询问星球x和y能不能互相到达。如果可以输出Yes,否则输出No。
Output
对于每个询问输出对应的答案。
Sample input&output
Input
4
3 1 1 0 2 1
2 1 0 2 1
3 0 1 0 1 3
4 0 0 1 1 2 4
12
2 1 2
2 1 4
1 5
2 1 11
1 6
1 7
1 8
2 2 8
1 6
2 2 8
1 10
2 2 8
Output
Yes
No
Yes
Yes
No
Yes
Hint
对于30%的数据 sigma Ci<=1000 , n<=100 , m<=10000
另有10%的数据 Ci<=500000,n=1 , m<=500000
对于100%的数据 sigmaCi<=500000 , n<=10000 , m<=500000 , Ci>1 , x!=y无论是询问还是起始结束星球
解题思路:
用两个树状数组维护,第一个树状数组按编号插入,维护小圈内各点是否通达(小圈内路径上的点编号相邻,如区间查询的值等于总点数则连通,另外要考虑反着走),第二个树状数组每个小圈开三个点分别记录小圈起始点,起始点和结束点是否连通,和结束点的信息,就可以用区间查询(原理同上)统计是否连通。修改时分别维护两个树状数组信息。
#pragma GCC optimize(2)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define low(x) (x&(-x))
using namespace std;
typedef long long ll;
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') ch=='-'&&(f=-1),ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=10010,M=5e5+10;
int n,tot,sum;bool val[M];
int *a[N],st[N],ed[N],siz[N],w[M],belong[M];
struct BIT
{
int c[M<<1];
void add(int x,int y,int k){for (;x<=k;x+=low(x)) c[x]+=y;}
int query(int x){int su=0;while (x) su+=c[x],x-=low(x);return su;}
}bit[2];
bool get(int x,int y)
{
if (x==y) return 1;
if (x>y) swap(x,y);
return (bit[0].query(y)-bit[0].query(x-1)==y-x+1)||
(bit[0].query(x)-bit[0].query(a[belong[x]][1]-1)+bit[0].query(a[belong[y]][siz[belong[y]]])-bit[0].query(y-1)==siz[belong[y]]-(y-x-1));
}//判小圈两点是否相通
bool get1(int x,int y)
{
if (x==y) return 1;
if (x>y) swap(x,y);
return (bit[1].query(y)-bit[1].query(x-1)==y-x+1)||(bit[1].query(x)+bit[1].query(3*n)-bit[1].query(y-1)==3*n-y+1+x);
}//判大圈两点是否相通
int main()
{
n=read();a[0]=&w[0];
for (int i=1;i<=n;++i)
{
siz[i]=read();
a[i]=a[i-1]+siz[i-1];
for (int j=1;j<=siz[i];++j)val[++sum]=read(),w[sum]=sum,belong[sum]=i;
st[i]=read(),ed[i]=read();
}
for (int i=1;i<=n;++i)
for (int j=1;j<=siz[i];++j)bit[0].add(a[i][j],val[a[i][j]],sum);
for (int i=1,x;i<=n;++i)
{
x=i*3;bit[1].add(x-1,get(a[i][st[i]],a[i][ed[i]]),n*3);
bit[1].add(x-2,val[a[i][st[i]]],n*3);
bit[1].add(x , val[a[i][ed[i]]],n*3);
}
for (int i=0,_=read(),x,y,mode;i<_;++i)
{
mode=read();
if (mode==1)
{
x=read();int u=belong[x];
if (x==a[u][st[u]])
bit[1].add(3*u-2,-val[x],n*3),bit[1].add(3*u-2,val[x]^1,n*3);
else if (x==a[u][ed[u]])
bit[1].add(3*u,-val[x],n*3),bit[1].add(3*u,val[x]^1,n*3);
bit[1].add(u*3-1,-get(a[u][st[u]],a[u][ed[u]]),n*3);
bit[0].add(x,-val[x],sum),bit[0].add(x,val[x]^1,sum);
bit[1].add(u*3-1,get(a[u][st[u]],a[u][ed[u]]),n*3),val[x]^=1;
}
if (mode==2)
{
x=read(),y=read();
int u=belong[x],v=belong[y];
if
(
(get1(3*u,3*v-2)&&get(x,a[u][ed[u]])&&get(y,a[v][st[v]]))||
(get1(3*v,3*u-2)&&get(x,a[u][st[u]])&&get(y,a[v][ed[v]]))
)puts("Yes");//先从大圈判,再从小圈判,否则会被卡,见下图
else if (u==v&&get(x,y)) puts("Yes");else puts("No");
}
}
return 0;
}
下图黑点表示可以通行,白点反之,双边表示大圈边,单边表示小圈边,此时A点和B点仍可连通