Mister B and Astronomers CodeForces - 819D(数论)

Mister B and Astronomers CodeForces - 819D(数论)

题目大意

有n个观察员,第一个观察员在0秒开始观察星空,随后第i个观察员会在第i-1个观察员之之后 a i a_i ai秒进行观察,第一个观察员也会在第n个观察员观察后 a 1 a_1 a1秒观察,有一颗星星其闪烁的周期为T,其第一次闪烁的时间不确定.问每个观察员有多少种可能成为第一个观察到这颗星星的人

解题思路

假设第i个观察员第一次观察的时刻为 b i b_i bi,观察员的观察周期为S,则本题实际上就是求解
( b i + k ∗ s ) % T = y ( y ∈ ( 0 , T − 1 ) ) (b_i+k*s)\%T=y(y\in (0,T-1)) (bi+ks)%T=y(y(0,T1))
对于每个y,使得k最小的 b i b_i bi

对此我们可以对原等式进行转化
b i % T + ( k ∗ s ) % T = y b_i\%T+(k*s)\%T=y bi%T+(ks)%T=y
其中 ( k ∗ s ) % T = q ∗ g c d ( s , T ) ( q ∈ Z + ) (k*s)\%T=q*gcd(s,T)(q\in Z^+) (ks)%T=qgcd(s,T)(qZ+)先令 d = g c d ( s , T ) d=gcd(s,T) d=gcd(s,T)因此凡是使得 b i % T % d = y % d b_i\%T\%d=y\%d bi%T%d=y%d的y均可被 b i b_i bi观察到,但还需要求为第一次观察到的.

继续分析.由上述的分析结果我们可以将所有的 b i % T b_i\%T bi%T分成d类由此每一类都只会在自己这一类中跳动,每类都有 T d \frac{T}{d} dT个元素

而所有的y也将分布在这些类中,因此所有的y也将分布在每类的任两个数之间

因此只需求出所有的类中的数与其下一个数之间的距离即可

其中为了方便求距离我们需要对每个数引入一个坐标,其中每个数的位置可以表示为 ( b i + k ∗ s ) % T % d = y % d ( y ∈ ( 0 , T − 1 ) ) (b_i+k*s)\%T\%d=y\%d(y\in (0,T-1)) (bi+ks)%T%d=y%d(y(0,T1))方程的解k

AC代码

#include<bits/stdc++.h>
using namespace std;
const int sz=2e5+5;
typedef long long LL;
unordered_map<int,int> mp,id;
unordered_map<int,bool> hs;
set<int> s[sz];
int vis[sz];
int arr[sz];
int nos[sz];
int ans[sz];
LL exgcd(LL a,LL b,LL &x,LL &y)	
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    LL ret=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return ret;
}
LL getInv(LL a,LL mod)
{
	LL x,y;
    exgcd(a,mod,x,y);
    return x;
}
int32_t main()
{
	int T,n;
	cin>>T>>n;
	int S=0;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++) cin>>arr[i],S=(S+arr[i])%T;
	arr[1]=0;
	for(int i=2;i<=n;i++) arr[i]=(arr[i]+arr[i-1])%T;
	for(int i=1;i<=n;i++) if(!hs.count(arr[i])) hs[arr[i]]=1,vis[i]=1;
	int d=__gcd(S,T);
	T/=d;S/=d;
	int inv=getInv(S,T);
	int no=0;
	for(int i=1;i<=n;i++)
	{
		int t=arr[i]%d;
		if(!id[t]) id[t]=++no;
		nos[i]=1LL*inv*(arr[i]-t)/d%T;
		s[id[t]].insert(nos[i]);
	}
	for(int i=1;i<=n;i++)
	{	
		if(!vis[i]) {ans[i]=0;continue;}
		int ids=id[arr[i]%d];
		set<int>::iterator p=s[ids].find(nos[i]);
		p++;
		if(p==s[ids].end()) ans[i]=(*s[ids].begin())+T-nos[i];
		else ans[i]=(*p)-nos[i];
	}
	for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值