D. Lost Arithmetic Progression
题意:
三个数组
A
,
B
,
C
A,B,C
A,B,C为等差数列,其中
C
=
A
⋂
B
C=A \bigcap B
C=A⋂B
给你
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中没有出现过,那么一定无解
如果全部出现过,应该:
- C C C的首项大于 B B B的首项, C C C的尾项小于 B B B的尾项
- 满足 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
- 确保 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
⌊dadc⌋2
#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
n∗m的城市里,每条路都有一个长度
l
l
l,现在城市里有一个小偷,小偷会在城市里移动,偷窃。但是你不知道小偷如何移动。有一个记录器记录着小偷(初始为0),每当他经过一条长
l
l
l的边,记录器上的值就会异或
l
l
l,如果小偷进行了一次偷窃,记录器会将目前记录的值反馈给你,并将记录结果清零。
请你自己设计一条路线长度,每次小偷偷窃时,你都能通过记录器反馈给你的值,判断哪个地方失窃了,路线总长度小于
48000
48000
48000
思路:
假设我们给
n
∗
m
n*m
n∗m个城市顺序标号,然后让城市
i
i
i和
j
j
j相连的道路长度设计为
i
⊕
j
i \oplus j
i⊕j
这样小偷从
u
u
u走到
v
v
v,最后显示的结果为
u
⊕
v
u \oplus v
u⊕v
构造很合理,但是这样用的路线总长度似乎太大了,是否能通过改变点值的顺序来让设计的边尽可能的小呢,比如我们一次只会改变二进制状态下的一位
我们先将问题转化成一维的问题,最初我们有初始节点
0
,
1
0,1
0,1
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];
}
}