联合省选 2021 A卷 题解

Day 1

卡牌游戏

这题是道签到题。

容易想到把所有的 a , b a,b a,b 拿出来从小到大排序。然后枚举最小值,确定最大值。

先讲一种很菜的 O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n) 做法。考虑 二分 最大值的位置。比这个最大值大的所有 a a a 显然都要跳到它们对应的 b b b 处,可能还会出现不合法的情况。因此可以先用 后缀 维护出排序后下标在 [ i , 2 n ] [i,2n] [i,2n] 中的 a a a 对应的 b b b 的最大值,和 a a a 的个数。

其实还可以 O ( n ) O(n) O(n) 双指针 。因为最小值指针右移时,最大值指针不会左移。

如果忍受不了排序那个 O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n) ,可以 双关键字桶排

矩阵游戏

考虑先不管上下限,求出一组解,然后再进行调整。

求这一组解很简单,可以令第一行、第一列全部为 0 0 0 。然后按顺序求出其他值即可。

怎么调整?发现对于任意一个 2 × 2 2\times 2 2×2 的矩阵,我们把其中的两个 − 1 -1 1 ,另外两个 + 1 +1 +1 ,它们的和不变,仍然满足 b b b 的限制。如果只修改一行,可以把这一行上列编号为奇数的元素 + x +x +x ,列编号为偶数的元素 − x -x x 。对于每一列也是同理。

但是为什么这样调整可以达到合法的状态?

发现根据 b b b 矩阵我们可以列出 ( n − 1 ) ( m − 1 ) (n-1)(m-1) (n1)(m1) 个方程,此时距离解出 a a a 的值还差 n + m − 1 n+m-1 n+m1 个方程。因此如果我们确定了第一行和第一列所有元素的值,矩阵中的其它 a a a 的值就是固定的了。而显然我们可以通过操作让第一行、第一列的每一个元素达到任意取值。

那么我们怎么调整?

设第 i i i 行调整的数为 c i c_i ci ,第 i i i 列调整的数为 d i d_i di 。那么
a i , j ′ = a i , j + ( − 1 ) j + 1 c i + ( − 1 ) i + 1 d j a'_{i,j}=a_{i,j} + (-1)^{j+1} c_{i} + (-1)^{i+1} d_{j} ai,j=ai,j+(1)j+1ci+(1)i+1dj
发现当 i , j i,j i,j 的奇偶性相同时, c i , d j c_i,d_j ci,dj 前面的系数相同。这时就是 和分约束 了,做不了。

因为我们只要让相邻的两个数 c , d c,d c,d 的系数不同即可,因此可以
a i , j ′ = a i , j + ( − 1 ) i + j − 1 c i + ( − 1 ) i + j d i a'_{i,j}=a_{i,j} +(-1)^{i+j-1}c_i + (-1)^{i+j}d_i ai,j=ai,j+(1)i+j1ci+(1)i+jdi
然后就是 差分约束 啦。

由于 差分约束 中我们建出来的是一个 完全二分图 ,因此用 B e l l m a n − F o r d Bellman-Ford BellmanFord 也可以跑得很快。

图函数

不难想到, f ( u , G ) f(u,G) f(u,G) 表示图 G G G 中有多少个点 v v v 满足同时存在 u → v u\to v uv v → u v\to u vu 且这两条路径上的点的编号都不小于 v v v

那么可以设 g i , j , k g_{i,j,k} gi,j,k 表示当前的图中是否存在路径 i → j i\to j ij 满足路径上的点的编号都不小于 k k k

如果用 F l o y d Floyd Floyd 做,可以省掉 k k k 这一维,从大到小枚举中转点 k k k 即可。

发现 G G G 很烦,想办法怎么一次 F l o y d Floyd Floyd 做完。

删边有点难搞,可以考虑把边按编号从大到小插入。发现我们的状态 g g g 记录一个 b o o l bool bool 值,比较浪费。于是设 h i , j h_{i,j} hi,j 表示当前的图中,所有 i → j i\to j ij 的路径中 最大路径上最小的边的编号 (省略 k k k 这一维)。

F l o y d Floyd Floyd 做,可以求出一个答案的 差分数组 ,求一个 后缀和 就好了。

虽然时间复杂度是 O ( n 3 ) O\left(n^3\right) O(n3) ,但是这却是这题最快的解法。

尽管如此,可能还要卡卡常。下面给出一个小优化:

F l o y d Floyd Floyd 时, i , j > k i,j>k i,j>k 的情况更新状态 h i , j h_{i,j} hi,j 没有意义了。

证明:

因为 i , j i,j i,j 不会再当中转点了,且这两个点都不会作为 k k k 对答案造成贡献了,所以更新状态 h i , j h_{i,j} hi,j 没有意义。

宝石

D a y    2 Day\; 2 Day2 签到题,然而我不会。

先考虑一条链上怎么做。我们先跳到 S → T S\to T ST 中离 S S S 最近的颜色为 P 1 P_1 P1 的点,然后跳离这个点最近的 P 2 P_2 P2 ,接着跳 P 3 P_3 P3 ……

那么现在要维护每个点后第一个颜色为 P 1 P_1 P1 的点,以及这个点在宝石收集器中的下一种颜色对应的点。由于要跳很多步,用倍增优化跳的过程。

现在放到树上,假设我们现在能 O ( 1 ) O(1) O(1) 求出任意一个点到根的路径上离它最近的某种颜色的点,剩下的怎么做呢?老套路,把 S → T S\to T ST 的路径分成 S → l c a , l c a → T S\to lca,lca\to T Slca,lcaT

先求在 S → l c a S\to lca Slca 上最多能收集多少个宝石。这不是直接倍增往上跳就行了吗?

再求在 l c a → T lca\to T lcaT 上最多能收集多少个宝石。不好做,于是把路径反过来。二分收集到的最后一颗宝石,预处理出每个点在宝石收集器中的上一种颜色对应的点,也是倍增优化。这样就可以 O ( log ⁡ 2 c log ⁡ 2 n ) O(\log_2 c\log_2 n) O(log2clog2n) 地求了。

发现这时这两部分可以分开求。

于是把询问离线,遍历这棵树两次,每次都动态维护当前点到根的路径上离它最近的某种颜色的点。第一次遍历时求出 S → l c a S\to lca Slca 这部分的答案,第二次遍历时求出 l c a → T lca\to T lcaT 这部分的答案,这样就可以了。

时间复杂度 O ( n log ⁡ 2 n + q log ⁡ 2 c log ⁡ 2 n ) O(n\log_2 n+q\log_2 c\log_2 n) O(nlog2n+qlog2clog2n)

P.S:这道题还可以用 点分治 做到 O ( n log ⁡ 2 n + q log ⁡ 2 n ) O(n\log_2 n+q\log_2 n) O(nlog2n+qlog2n)

滚榜

一眼看上去就是状压 D P DP DP

f s , i , j , k f_{s,i,j,k} fs,i,j,k 表示状态 s s s 中的队伍还没有公布,上一个公布的队伍为 i i i b i = j b_i=j bi=j ,剩余的 m m m 值为 k k k

空间复杂度 O ( 2 n m 2 n ) O\left(2^n m^2 n\right) O(2nm2n) ,时间复杂度 O ( 2 n − 2 m 2 n 2 ) O\left(2^{n-2}m^2 n^2\right) O(2n2m2n2) ,勉强能拿 40 40 40 分,还不如暴力 O ( n × n ! ) O(n\times n!) O(n×n!)

考虑消掉一维,去掉 j j j 。由于 b b b 按公布顺序单调不下降,可以在每次加入一个队伍 x x x 之后强制把没有公布的队伍的 b b b 都加上 max ⁡ ( a i − a x + [ x > i ] , 0 ) \max\left(a_i-a_x+[x>i],0\right) max(aiax+[x>i],0) ,这样就行了。

空间复杂度 O ( 2 n m n ) O\left(2^nmn \right) O(2nmn),时间复杂度 O ( 2 n − 2 m n 2 ) O\left(2^{n-2}mn^2\right) O(2n2mn2) (实际上时间肯定是跑不满的)。

支配

考场上觉得这题可做,就拼命弄这题,结果因为一些地方想错了,最后只拿到树的 15 15 15 分。

发现对于每个点 x x x ,图中必定存在一条链,使得链上包含所有在 D x D_x Dx 中的点。若构建一个新图 G 1 G_1 G1 ,若原图 G 0 G_0 G0 x x x D y D_y Dy 中离 y y y 最近的点(即走最少的步数能到达 y y y 的点),就在 G 1 G_1 G1 中加入一条边 x → y x\to y xy 。发现 G 1 G_1 G1 会变成一棵根节点是 1 1 1 的树。事实上,这就是 支配树

怎么建这棵树?有 O ( m ) O(m) O(m) 的做法,但是因为这题允许我们 O ( n 2 ) O\left(n^2\right) O(n2) 建树,就不如用好写的 O ( n 2 ) O\left(n^2\right) O(n2) 做法了。

可以从 1 1 1 n n n 枚举一个点 i i i 。然后从 1 1 1 出发,不经过 i i i 遍历整个图。若最后点 j j j 没有被遍历到,那么 i ∈ D j i\in D_j iDj 。显然 i i i 的父亲就是 D i D_i Di ∣ D ∣ |D| D 最小的点。

接下来考虑怎么求答案。

显然就是求加入 s → t s\to t st 后有多少个点的受支集合发生改变(显然受支集合只会减小,即去除某些元素)。假设某个点 i i i 在支配树上的子树中的所有点的 D D D 发生了改变(这显然是因为 D i D_i Di 中一些元素不在 D s D_s Ds 中),可以发现 i i i 要满足两个条件:

  1. f a i fa_i fai 不在支配树中 1 → s 1\to s 1s 的路径上,否则 D s ⊆ D i D_s\subseteq D_i DsDi
  2. 原图中 t t t 不经过 f a i fa_i fai 就可以到达 i i i ,否则新增的 1 → i 1\to i 1i 的路径仍然必经 D i D_i Di 中的每一个点。

可以把原图中的边反向,对于点 i i i ,求出有哪些点 j j j 不经过 f a j fa_j faj 就能到达 i i i 。就可以求出所有满足 条件2 的点了。

枚举这些点,筛选出其中满足 条件1 的点即可。

但是这些点可能已经在支配树上形成祖孙关系了,这个差分处理即可。

时间复杂度 O ( n 2 + n q ) O\left(n^2 +nq \right) O(n2+nq)


代码

card

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define M 2000005
#define N 1000005
const int inf=1000000001;
int m,s,a[N][2],minn[M],maxx[M],sum[M];bool state[M];
struct node{int num,id;bool type;}b[M];
bool cmp(node x,node y){return x.num<y.num;}
inline int mymin(int x,int y){return x<y?x:y;}
inline int mymax(int x,int y){return x>y?x:y;}
int main()
{
	freopen("card.in","r",stdin);
	freopen("card.out","w",stdout);
	register int i;
	int n,ans=inf,l,r,mid,L=1,res;
	scanf("%d%d",&n,&m);
	fo(i,1,n) scanf("%d",&a[i][0]),b[++s]=(node){a[i][0],i,0};
	fo(i,1,n) scanf("%d",&a[i][1]),b[++s]=(node){a[i][1],i,1};
	sort(b+1,b+s+1,cmp);
	fo(i,1,s) a[b[i].id][b[i].type]=i;
	minn[s+1]=inf;
	for(i=s;i;--i)
	{
		if(b[i].type)
			minn[i]=minn[i+1],maxx[i]=maxx[i+1],sum[i]=sum[i+1];
		else
		{
			minn[i]=mymin(minn[i+1],a[b[i].id][1]);
			maxx[i]=mymax(maxx[i+1],a[b[i].id][1]);
			sum[i]=sum[i+1]+1;
		}
	}
	fo(i,1,s)
	{
		if(b[i].type&&!state[b[i].id]) --m,state[b[i].id]=1;
		if(m<0) goto end;
		l=L,r=s,res=-1;
		while(l<=r)
		{
			mid=l+r>>1;
			if(minn[mid+1]<i||maxx[mid+1]>mid||sum[mid+1]-(b[i].type&&a[b[i].id][0]>mid?1:0)>m) l=mid+1;
			else r=mid-1,res=mid;
		}
		if(res>0&&b[res].num-b[i].num<ans) ans=b[res].num-b[i].num;
		end:
		if(b[i].type)
		{
			if(a[b[i].id][0]<i) break;
			if(a[b[i].id][0]>L) L=a[b[i].id][0];
			++m,state[b[i].id]=0;
		}
		else
		{
			if(a[b[i].id][1]<i) break;
			if(a[b[i].id][1]>L) L=a[b[i].id][1];
			--m,state[b[i].id]=1;
		}
	}
	printf("%d\n",ans);
	return 0;
}

matrix

#include<cstdio>
using namespace std;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define fd(i,r,l) for(i=r;i>=l;--i)
#define N 305
char p[15],buf[100005],*l=buf,*r=buf;
inline char gc()
{return l==r&&(r=(l=buf)+fread(buf,1,100005,stdin),l==r)?EOF:*l++;}
inline void read(int &k)
{
	char ch;while(ch=gc(),ch<'0'||ch>'9');k=ch-48;
	while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-48;
}
inline void print(int x)
{
	if(!x) putchar('0');
	else
	{
		int len=0;
		while(x) p[++len]=x%10+48,x/=10;
		for(;len;--len) putchar(p[len]);
	}
	putchar(' ');
}
int a[N][N],b[N][N],wa[N][N],wb[N][N],fa[N],fb[N];
inline void mymin(int &x,int y){if(x>y) x=y;}
int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	register int i,j;
	int T,n,m,s,x,y;
	bool flag;
	read(T);
	while(T--)
	{
		read(n),read(m);
		for(i=1;i<n;++i)
			for(j=1;j<m;++j) read(b[i][j]);
		fo(i,1,n) a[i][1]=0;
		fo(i,1,m) a[1][i]=0;
		fo(i,2,n) fo(j,2,m) a[i][j]=b[i-1][j-1]-a[i][j-1]-a[i-1][j]-a[i-1][j-1];
		s=n+m;
		fo(i,1,n) fo(j,1,m) wa[i][j]=wb[j][i]=0x3f3f3f3f;
		fo(i,1,n) fa[i]=0;
		fo(i,1,m) fb[i]=0;
		fo(i,1,n) fo(j,1,m)
		{
			if(i+j&1) // d[j]-c[i]
			{
				mymin(wa[i][j],a[i][j]),
				mymin(wb[j][i],1000000-a[i][j]);
			}
			else // c[i]-d[j]
			{
				mymin(wb[j][i],a[i][j]),
				mymin(wa[i][j],1000000-a[i][j]);
			}
		}
		fo(x,1,s)
		{
			flag=1;
			fo(i,1,n) fo(j,1,m)
				if(fa[i]>fb[j]+wb[j][i])
					fa[i]=fb[j]+wb[j][i],flag=0;
			fo(i,1,m) fo(j,1,n)
				if(fb[i]>fa[j]+wa[j][i])
					fb[i]=fa[j]+wa[j][i],flag=0;
			if(flag) break;
		}
		if(x>s) goto out;
		fo(i,1,n) fo(j,1,m)
		{
			a[i][j]=i+j&1?a[i][j]+fa[i]-fb[j]:a[i][j]-fa[i]+fb[j];
			if(a[i][j]<0||a[i][j]>1000000) goto out;
		}
		puts("YES");
		fo(i,1,n)
		{
			fo(j,1,m) print(a[i][j]);
			putchar('\n');
		}
		continue;
		out:puts("NO");
	}
	return 0;
}

graph

#include<cstdio>
using namespace std;
#define M 200005
#define N 1005
char p[15],buf[100005],*pl=buf,*pr=buf;
inline char gc()
{return pl==pr&&(pr=(pl=buf)+fread(buf,1,100005,stdin),pl==pr)?EOF:*pl++;}
inline void read(int &k)
{
	char ch;while(ch=gc(),ch<'0'||ch>'9');k=ch-48;
	while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-48;
}
inline void print(int x)
{
	if(!x) putchar('0');
	else
	{
		int len=0;
		while(x) p[++len]=x%10+48,x/=10;
		for(;len;--len) putchar(p[len]);
	}
	putchar(' ');
}
int f[N][N],ans[M];
inline int mymin(int x,int y){return x<y?x:y;}
inline void mymax(int &x,int y){if(x<y) x=y;}
int main()
{
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	register int i,j,k,tmp;
	int x,y,n,m;
	read(n),read(m);
	for(i=1;i<=m;++i) read(x),read(y),f[x][y]=i;
	for(k=n;k;--k)
	{
		for(i=k+1;i<=n;++i) ++ans[mymin(f[i][k],f[k][i])];
		for(i=1;i<k;++i) if(f[i][k])
			for(j=1;j<=n;++j)
				mymax(f[i][j],mymin(f[i][k],f[k][j]));
		for(i=k+1;i<=n;++i) if(f[i][k])
			for(j=1;j<k;++j)
				mymax(f[i][j],mymin(f[i][k],f[k][j]));
		// i,j>k 因为 i,j 不会再当中转点了,且这两个点都不会作为 k 对答案造成贡献了,所以没有意义 
	}
	ans[m+1]=n;
	for(i=m;i;--i) ans[i]+=ans[i+1];
	for(i=1;i<=m+1;++i) print(ans[i]);
	return 0;
}

gem

#include<cstdio>
#include<vector>
using namespace std;
char p[15],buf[100005],*pl=buf,*pr=buf;
inline char gc()
{return pl==pr&&(pr=(pl=buf)+fread(buf,1,100005,stdin),pl==pr)?EOF:*pl++;}
inline void read(int &k)
{
	char ch;while(ch=gc(),ch<'0'||ch>'9');k=ch-48;
	while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-48;
}
inline void print(int x)
{
	if(!x) putchar('0');
	else
	{
		int len=0;
		while(x) p[++len]=x%10+48,x/=10;
		for(;len;--len) putchar(p[len]);
	}
	putchar('\n');
}
#define fo(i,l,r) for(i=l;i<=r;++i)
#define M 400005
#define N 200005
#define C 50005
int fir[N],to[M],nex[M],w[N],num[C],id[C],las[C],f[N][18],g[N][18],h[N][18],dep[N],c,q,s,l,r,mid,now,res;
struct node{int st,ed,lca,mid,ans;}a[N];
vector<int>qry1[N],qry2[N];
inline void inc(int x,int y)
{
	to[++s]=y,nex[s]=fir[x],fir[x]=s;
	to[++s]=x,nex[s]=fir[y],fir[y]=s;
}
void dfs(int k)
{
	int i,tmp=las[w[k]];
	dep[k]=dep[f[k][0]]+1;
	g[k][0]=las[num[id[w[k]]+1]],
	h[k][0]=id[w[k]]?las[num[id[w[k]]-1]]:0;
	las[w[k]]=k;
	for(i=fir[k];i;i=nex[i])
		if(to[i]!=f[k][0])
			f[to[i]][0]=k,dfs(to[i]);
	las[w[k]]=tmp;
}
inline void swap(int &x,int &y){int z=x;x=y,y=z;}
inline int getlca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=17;i>=0;--i)
		if(dep[f[u][i]]>=dep[v])
			u=f[u][i];
	if(u==v) return u;
	for(int i=17;i>=0;--i)
		if(f[u][i]!=f[v][i])
			u=f[u][i],v=f[v][i];
	return f[u][0];
}
void solveL(int k)
{
	int i,j,tmp;
	for(i=0;i<qry1[k].size();++i)
	{
		now=qry1[k][i],
		tmp=w[k]==num[1]?k:las[num[1]];
		for(j=17;j>=0;--j)
			if(dep[g[tmp][j]]>=dep[a[now].lca])
				tmp=g[tmp][j];
		a[now].mid=dep[tmp]>=dep[a[now].lca]?w[tmp]:0;
	}
	tmp=las[w[k]],las[w[k]]=k;
	for(i=fir[k];i;i=nex[i])
		if(to[i]!=f[k][0]) solveL(to[i]);
	las[w[k]]=tmp;
}
void solveR(int k)
{
	int i,j,tmp;
	for(i=0;i<qry2[k].size();++i)
	{
		now=qry2[k][i],l=id[a[now].mid]+1,r=c,res=l-1;
		while(l<=r)
		{
			mid=l+r>>1,
			tmp=w[k]==num[mid]?k:las[num[mid]];
			for(j=17;j>=0;--j)
				if(dep[h[tmp][j]]>=dep[a[now].lca])
					tmp=h[tmp][j];
			if(dep[tmp]>=dep[a[now].lca]&&id[w[tmp]]<=id[a[now].mid]+1) res=mid,l=mid+1;
			else r=mid-1;
		}
		a[now].ans=res;
	}
	tmp=las[w[k]],las[w[k]]=k;
	for(i=fir[k];i;i=nex[i])
		if(to[i]!=f[k][0]) solveR(to[i]);
	las[w[k]]=tmp;
}
int main()
{
	freopen("gem.in","r",stdin);
	freopen("gem.out","w",stdout);
	register int i;
	int j,n,m,x,y;
	read(n),read(m),read(c);
	fo(i,1,c) read(num[i]),id[num[i]]=i;
	fo(i,1,n) read(w[i]);
	for(i=1;i<n;++i) read(x),read(y),inc(x,y);
	dfs(1);
	fo(j,1,17) fo(i,1,n)
		f[i][j]=f[f[i][j-1]][j-1],
		g[i][j]=g[g[i][j-1]][j-1],
		h[i][j]=h[h[i][j-1]][j-1];
	read(q);
	fo(i,1,q)
		read(a[i].st),read(a[i].ed),
		a[i].lca=getlca(a[i].st,a[i].ed),
		qry1[a[i].st].push_back(i),
		qry2[a[i].ed].push_back(i);
	fo(i,1,m) las[i]=0;solveL(1);
	fo(i,1,m) las[i]=0;solveR(1);
	fo(i,1,q) print(a[i].ans);
	return 0;
}

ranklist

#include<cstdio>
using namespace std;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define fd(i,r,l) for(i=r;i>=l;--i)
#define M 505
#define N 15
long long f[8195][N][M];
int a[N];
int main()
{
	freopen("ranklist.in","r",stdin);
	freopen("ranklist.out","w",stdout);
	register int k,j,i;
	int s,t,len,n,m,S,tmp,max=0;
	long long ans=0;
	scanf("%d%d",&n,&m),a[0]=-1;
	fo(i,1,n) scanf("%d",a+i);
	fo(i,1,n) if(a[i]>a[max]) max=i;
	S=(1<<n)-1;
	fo(i,1,n)
	{
		tmp=n*(a[max]-a[i]+(max<i));
		if(tmp<=m) f[S^1<<i-1][i][m-tmp]=1;
	}
	fd(s,S-1,1)
	{
		len=0;
		fo(i,1,n) if(s&1<<i-1) ++len;
		fo(i,1,n) if(!(s&1<<i-1))
			fo(k,1,n) if(s&1<<k-1)
			{
				tmp=(a[i]-a[k]+(i<k))*len;
				t=s^1<<k-1;
				if(tmp<0) tmp=0;
				fo(j,tmp,m) if(f[s][i][j])
					f[t][k][j-tmp]+=f[s][i][j];
			}
	}
	fo(i,1,n) fo(j,0,m) ans+=f[0][i][j];
	printf("%lld\n",ans);
	return 0;
}

dominator

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define M 6005
#define N 3005
struct graph
{
	int fir[N],to[M],nex[M],s;
	inline void inc(int x,int y)
	{to[++s]=y,nex[s]=fir[x],fir[x]=s;}
}G,F,D_tree;
struct node
{
	int D[N],siz;
	inline void inc(int x){D[++siz]=x;}
}a[N],list[N];
int fa[N],vis[N],siz[N],dfn[N],sum[N],ord,time;
bool b[N],in[N];
void dfs1(int k)
{
	vis[k]=time;
	for(int i=G.fir[k];i;i=G.nex[i])
		if(b[G.to[i]]&&vis[G.to[i]]<time)
			dfs1(G.to[i]);
}
void dfs2(int k)
{
	vis[k]=time,list[k].inc(time);
	for(int i=F.fir[k];i;i=F.nex[i])
		if(b[F.to[i]]&&vis[F.to[i]]<time)
			dfs2(F.to[i]);
}
void dfs3(int k)
{
	siz[k]=1,dfn[k]=++ord;
	for(int i=D_tree.fir[k];i;i=D_tree.nex[i])
		dfs3(D_tree.to[i]),siz[k]+=siz[D_tree.to[i]];
}
int main()
{
	freopen("dominator.in","r",stdin);
	freopen("dominator.out","w",stdout);
	register int i,j;
	int n,m,q,x,y,ans,k;
	scanf("%d%d%d",&n,&m,&q);
	fo(i,1,m)
		scanf("%d%d",&x,&y),
		G.inc(x,y),F.inc(y,x);
	fo(i,1,n) b[i]=1;
	fo(i,2,n)
	{
		a[i].inc(1);
		time=i,b[i]=0,
		dfs1(1),vis[i]=i;
		fo(j,2,n) if(vis[j]<i)
			a[j].inc(i);
		b[i]=1;
	}
	fo(i,2,n)
	{
		fa[i]=a[i].D[1];
		fo(j,2,a[i].siz)
			if(a[a[i].D[j]].siz>a[fa[i]].siz)
				fa[i]=a[i].D[j];
	}
	fo(i,2,n) D_tree.inc(fa[i],i);
	dfs3(1);
	fo(i,1,n) vis[i]=0;
	fo(i,2,n)
	{
		time=i,b[fa[i]]=0;
		dfs2(i),b[fa[i]]=1;
	}
	b[0]=1;
	while(q--)
	{
		scanf("%d%d",&x,&y),ans=0;
		fo(i,1,n) sum[i]=0;
		i=x,b[1]=0;while(i>1) b[i]=0,i=fa[i];
		fo(j,1,list[y].siz)
		{
			k=list[y].D[j];
			if(b[fa[k]]) ++sum[dfn[k]],--sum[dfn[k]+siz[k]];
		}
		fo(j,1,n)
		{
			sum[j]+=sum[j-1];
			if(sum[j]) ++ans;
		}
		printf("%d\n",ans);
		i=x,b[1]=1;while(i>1) b[i]=1,i=fa[i];
	}
	return 0;
}
  • 1
    点赞
  • 6
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值