Codeforces Round #785 (Div. 2)(D,F)

D. Lost Arithmetic Progression

题意:
三个数组 A , B , C A,B,C A,B,C为等差数列,其中 C = A ⋂ B C=A \bigcap B C=AB
给你 B , C B,C B,C的首项,差值,长度,问符合题意的 A A A有多少种构成方式,或者判断是否无解或者无限解
思路:
在这里插入图片描述
显然 d c = L C M ( d a , d b ) d_c=LCM(d_a,d_b) dc=LCM(da,db)
首先,如果 C C C中如果有元素在 B B B中没有出现过,那么一定无解
如果全部出现过,应该:

  1. C C C的首项大于 B B B的首项, C C C的尾项小于 B B B的尾项
  2. 满足 d c = L C M ( d a , d b ) d_c=LCM(d_a,d_b) dc=LCM(da,db),等价于 d c % d b = 0 d_c\%d_b=0 dc%db=0
  3. 确保 C C C的首项在 B B B中会出现

如果 B B B中元素长度恰好只能刚刚覆盖 C C C,也就是只要 C C C向前后任意拓展一位, B B B就无法覆盖的话,就意味着 A A A可以在这个方向上任意的变长,而不用担心会和 B B B相遇,此时答案无限
最后剩余的情况答案一定是有限的,由于 A A A的公差一定满足上述要求,于是我们可以在 O ( d c ) O(\sqrt{d_c}) O(dc )的时间内枚举出 d a d_a da的全部可能,此时 A A A向左右各可拓展 ⌊ d c d a ⌋ \left\lfloor\dfrac{d_c}{d_a}\right\rfloor dadc个,总产生的新个数为 ⌊ d c d a ⌋ 2 \left\lfloor\dfrac{d_c}{d_a}\right\rfloor^2 dadc2

#include<bits/stdc++.h>
#define int long long
using namespace std;
int mod=1e9+7;
signed main()
{
	int T;
	scanf("%lld",&T);
	while(T--)
	{
		int bs,bd,bl,cs,cd,cl;
		scanf("%lld%lld%lld%lld%lld%lld",&bs,&bd,&bl,&cs,&cd,&cl);
		int be=bs+bd*bl-bd,ce=cs+cd*cl-cd;
		if(bs>cs || be<ce || cd%bd || (cs-bs)%bd)
		{
			puts("0");continue;
		}
		int pre=cs-cd,nex=ce+cd;
		if(pre<bs || nex>be)
		{
			puts("-1");continue;
		}
		int ans=0;
		for(int i=1;i*i<=cd;i++)
		{
			if(cd%i) continue;
			if(i*bd/__gcd(i,bd)==cd)
			{
				int num=cd/i;
				ans+=(num*num)%mod;
				ans%=mod;
			}
			if(i*i!=cd)
			{
				int num=cd/i;
				if(num*bd/__gcd(num,bd)==cd)
				ans+=(cd/num*cd/num);
				ans%=mod;
			}
		}
		printf("%lld\n",ans);
	}
}

F. Anti-Theft Road Planning

题意:
交互题,在 n ∗ m n*m nm的城市里,每条路都有一个长度 l l l,现在城市里有一个小偷,小偷会在城市里移动,偷窃。但是你不知道小偷如何移动。有一个记录器记录着小偷(初始为0),每当他经过一条长 l l l的边,记录器上的值就会异或 l l l,如果小偷进行了一次偷窃,记录器会将目前记录的值反馈给你,并将记录结果清零。
请你自己设计一条路线长度,每次小偷偷窃时,你都能通过记录器反馈给你的值,判断哪个地方失窃了,路线总长度小于 48000 48000 48000
思路:
假设我们给 n ∗ m n*m nm个城市顺序标号,然后让城市 i i i j j j相连的道路长度设计为 i ⊕ j i \oplus j ij
这样小偷从 u u u走到 v v v,最后显示的结果为 u ⊕ v u \oplus v uv
构造很合理,但是这样用的路线总长度似乎太大了,是否能通过改变点值的顺序来让设计的边尽可能的小呢,比如我们一次只会改变二进制状态下的一位
我们先将问题转化成一维的问题,最初我们有初始节点 0 , 1 0,1 01
0000 0001 0000\\0001 00000001
为了使变换尽可能的少,只需要将这个数组复制一遍,然后将其倒序,就可以无缝衔接
0000 0001 0001 0000 0000\\0001\\0001\\0000 0000000100010000
为了避免点重复,我们再将刚刚复制的最高位变为 1 1 1,于是我们就构造完成了,每条边的变化只有二进制上的一位
0000 0001 0011 0010 0000\\0001\\0011\\0010 0000000100110010
以此类推
0000 0001 0011 0010 0110 0111 0101 0100 . . . 0000\\0001\\0011\\0010\\0110\\0111\\0101\\0100\\... 00000001001100100110011101010100...
回到二维,只要构造好的矩阵,右边,下边,右下边分别翻转,分别加 01 , 10 , 11 01,10,11 01,10,11即可
构造如下
00000   00010   01010   01000  ... 00100   00110   01110   01100  ... 10100   10110   11110   11100  ... 10000   10010   11010   11000  ... . . . . 00000\text{ }00010\text{ }01010\text{ }01000\text{ ...}\\ 00100\text{ }00110\text{ }01110\text{ }01100\text{ ...}\\ 10100\text{ }10110\text{ }11110\text{ }11100\text{ ...}\\ 10000\text{ }10010\text{ }11010\text{ }11000\text{ ...}\\ .... 00000 00010 01010 01000 ...00100 00110 01110 01100 ...10100 10110 11110 11100 ...10000 10010 11010 11000 .......
这样的构造叫做格雷码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
map<int,PII>vis;
int a[100][100];
int main()
{
	int n,k;
	cin>>n>>k;
	int len=1;
	vis[0]={1,1};
	for(int p=0;len<n;p+=2,len*=2)
	{
		for(int i=1;i<=len;i++)
		{
			for(int j=1;j<=len;j++)
			{
				a[i][len*2-(j-1)]=a[i][j]+(1<<p);
				vis[a[i][j]+(1<<p)]={i,len*2-(j-1)};
				a[len*2-(i-1)][j]=a[i][j]+(1<<(p+1));
				vis[a[i][j]+(1<<(p+1))]={len*2-(i-1),j};
				a[len*2-(i-1)][len*2-(j-1)]=a[i][j]+(1<<p)+(1<<(p+1));
				vis[a[i][j]+(1<<p)+(1<<(p+1))]={len*2-(i-1),len*2-(j-1)};
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<n;j++)
		{
			cout<<(a[i][j]^a[i][j+1])<<" ";
		}
		cout<<endl;
	}
	for(int i=1;i<n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cout<<(a[i][j]^a[i+1][j])<<" ";
		}
		cout<<endl;
	}
	int last=0;
	while(k--)
	{
		int x;
		cin>>x;
		x^=last;
		cout<<vis[x].first<<" "<<vis[x].second<<endl;
		last=a[vis[x].first][vis[x].second];
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值