CF819D 题解
复读一遍 CF 题解。的确是一种非常妙的理解方式。
考虑同一个天文学家会在那些
显然,如果设
接下来考虑如何统计答案,我们考虑每一位天文学家会第一次“到达”哪些余数。
考虑挖掘一点性质,我们来将每一个天文学家的路径转化到图上。
如果我们把每一个余数
-
图是由
\gcd(S,T) 个大小为\dfrac{T}{\gcd(S,T)} 的环组成的。 -
每一个环内的数都
\text{mod} \gcd(S,T) 同余。
既然被划成了若干个环,那我们来考虑多个天文学家在同一个环中的答案情况。
首先,如果有多个天文学家的
图画出来也不难发现,每一个天文学家的答案就是它沿着环的方向到环上的下一个天文学家的距离(如果环内只有一个点就是环的大小)。
现在的问题在于,如何求出上面所说的那个距离。
我们考虑给每个环定义一个起始点,下标为
很明显,每个环的起始点选
如何求出某个余数
的最小自然数解,用 exgcd 求解这个同余方程即可。
最后遍历每一个环,对每一个天文学家求出答案就完了。
代码:
注意特判一个环中只有一个天文学家的情况。
#include <map>
#include <cstdio>
#include <vector>
#include <algorithm>
typedef long long ll;
const int maxn = 2e5+5;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll st[maxn],S,d,x,y;
int n,T,a[maxn],ans[maxn];
struct pair{
int pos,id;
pair(int X=0,int Y=0):pos(X),id(Y){};
bool operator <(pair b)const{return pos<b.pos;}
};
std :: map<int,bool> vis;
std :: map<int,std::vector<pair>> cys;
void exgcd(ll a,ll b,ll &x,ll &y){
if(!b)return x=1,y=0,void();
exgcd(b,a%b,x,y);ll t = x;
x = y,y = t-y*(a/b);
}
int main(){
scanf("%d %d",&T,&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
st[1] = 0;for(int i=2;i<=n;++i)st[i] = (st[i-1]+a[i])%T;
S = (st[n]+a[1])%T,d = gcd(S,T);
for(int i=1;i<=n;++i){
if(!vis[st[i]]){
vis[st[i]] = 1;
exgcd(S,T,x,y),x *= (st[i]-st[i]%d)/d,y *= (st[i]-st[i]%d)/d;
x = (x%(T/d)+T/d)%(T/d),cys[st[i]%d].push_back(pair(x,i));
}
}
std :: map<int,std::vector<pair>> :: iterator it = cys.begin();
for(;it != cys.end();++it){
std :: sort(it->second.begin(),it->second.end());
std :: vector <pair> :: iterator It = it->second.begin(),nxt = It;
while(++nxt,nxt != it->second.end())ans[It->id] = nxt->pos-It->pos,It = nxt;
--nxt,ans[nxt->id] = (it->second.size()==1?T/d:(it->second.begin()->pos-nxt->pos+(T/d))%(T/d));
}
for(int i=1;i<=n;++i)printf("%d ",ans[i]);
return 0;
}