洛谷 P10034 「Cfz Round 3」Circle

25 篇文章 0 订阅
5 篇文章 0 订阅

[Problem   Discription] \color{blue}{\texttt{[Problem Discription]}} [Problem Discription]

在这里插入图片描述

[Solution] \color{blue}{\texttt{[Solution]}} [Solution]

这是道好题。

建图,对每一个 i → p i i \to p_{i} ipi 都建立一个有向边,就可以得到一个 n n n 个点 n n n 条边组成的图。题目所需要满足的条件可以等价于从任何一个 S i S_{i} Si 1 \texttt{1} 1 的边出发走 l l l 步都能回到自身,且图中不存在自环。

显然,如果存在一个大小为 g g g 的环(点数一定等于边数,因为每个点的入度和出度均为 1 1 1),且 g g g l l l 的约数,那么这个环上的任意一点均可以走 l l l 步后回到自身。

根据这个想法,我们就可以判定是否有解:把 l l l 所有的大于 1 1 1 的约数找出来,假设记为 d 1 , d 2 , d 3 , ⋯   , d m d_{1},d_{2},d_{3},\cdots,d_{m} d1,d2,d3,,dm,有解等价于存在自然数(包括 0 0 0)序列 a 1 … m a_{1 \dots m} a1m,满足:

∑ i = 1 m a i d i = β \sum\limits_{i=1}^{m} a_{i}d_{i}=\beta i=1maidi=β

其中 β \beta β k k k n n n 的任意一个整数( k k k 表示 S S S 1 \texttt{1} 1 的点的个数)。 β \beta β 表示我们需要将有限制的 k k k 个点和无限制的点中任意的 ( β − k ) (\beta - k) (βk) 个一起组成若干个大小为 d i d_{i} di 的环(如果 a i a_{i} ai 0 0 0,则不存在大小为 d i d_{i} di 的环)。假设环的大小分别为 D 1 , D 2 , ⋯   , D M D_{1},D_{2},\cdots,D_{M} D1,D2,,DM

显然,我们用背包即可判断是否有解。同时还可以知道我们需要构造哪些大小的环。

注意: β \beta β 不能等于 n − 1 n-1 n1。如果 β = n − 1 \beta=n-1 β=n1,那么剩下的那一个没有限制的点需要形成自环,不合题意。

如果判断有解,剩下的事情就比较简单了:只要依次挑出 D i D_{i} Di 个点首尾相连形成大小为 D i D_{i} Di 的环即可。这部分比较简单,具体看代码。

[Notice] \color{blue}{\texttt{[Notice]}} [Notice]

  1. 如果 k = 0 k=0 k=0,即没有有限制的点,那么一定有解( 1 → 2 , 2 → 3 , ⋯   , n → 1 1 \to 2,2 \to 3, \cdots, n \to 1 12,23,,n1 即可)。不然会 WA on test 10 10 10
  2. 只有 ( n − 1 ) (n-1) (n1) 是不合要求的,大小为 n n n 的环是符合要求的。写代码时要注意这一点,不然会 WA on test 13 13 13
  3. dp 的时候找到了解就要及时退出,减少代码运行量,不然会 TLE on test 14 14 14
  4. 记得及时清空数组,毕竟是多测。

Code \color{blue}{\text{Code}} Code

const int N=5e5+100;

bool f[N],limit[N];
char s[N];long long l;
int T,n,k,p[N],lst[N];
int Div[N],dcnt,beg;
int unlimit[N];

void initdata(){
	for(int i=1;i<=n;i++){
		p[i]=lst[i]=Div[i]=0;
		f[i]=limit[i]=false;
		unlimit[i]=0;
	}
	k=dcnt=beg=0;
}

int main(){
	scanf("%d",&T);
	while (T--){
		scanf("%d%lld",&n,&l);
		scanf("%s",s+1);
		
		initdata();
		
		for(int i=1;i<=n;i++)
			if (s[i]=='1'){
				k++;//统计有限制的点的个数
				limit[i]=true;
			}
		
		for(int i=2;i<=n;i++)
			if (l%i==0&&i!=n-1){
				f[i]=true;
				Div[++dcnt]=i;
				lst[i]=i;//别忘了这个
			}
		
		for(int i=1,flag=1;i<=dcnt&&flag;i++)
			for(int j=Div[i];j<=n;j++)
				if (f[j-Div[i]]&&!f[j]){
					f[j]=true;
					lst[j]=Div[i];
					if (k<=j&&j<=n&&j!=n-1){
						flag=1;break;//及时退出
					}
				}
		
		for(int i=k;i<=n;i++)
			if (f[i]&&i!=n-1){
				beg=i;//对应解析里的 beta
				break;
			}
		
		if (beg!=0){
			for(int i=1,l=0;l<n-beg;i++)
				if (!limit[i]) unlimit[++l]=i;
			if (beg!=n){
				for(int i=1;i<n-beg;i++)
					p[unlimit[i]]=unlimit[i+1];
				p[unlimit[n-beg]]=unlimit[1];
			}//多余的无限制的点也要连成环(不过环的大小没限制啦是真的)
			
			int l=0,r=0,cnt=0,tmp=beg;
			for(int i=1;i<=n;i++)
				if (!p[i]){
					if ((++cnt)==lst[tmp]){
						p[r]=i;
						tmp-=lst[tmp];
						cnt=0;
					}
					
					p[i]=l;l=i;
					if (cnt==1) r=i;
				}//构造解
		}
		
		if (k==0){
			for(int i=1;i<n;i++)
				printf("%d ",i+1);
			printf("1\n");
		}
		else if (!beg) printf("-1\n");
		else{
			for(int i=1;i<=n;i++)
				printf("%d%c",p[i],(i==n?'\n':' '));
		}
	}
	
	return 0;
}

Wish You Good Luck!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值