题意:
有T个石子,n个人,每个人轮流取石子,一颗石子只能取一次,求每个人能取到多少石子。
假设i-1
号取的位置为pos
,那么i
号取的位置就是 (pos+ai)modT ,如果当前位置的石子已经被取走,这个人的所取石子数量不增加,i+1
取的位置为 (pos+ai+ai+1)modT
算法:
设
- S=∑ni=1ai ,
- sti=∑ni=2ai ,为每个人第一次取得位置,不考虑那些第一次就取不到的人。那么每个人可以取到的就是 (sti+S)modT,(sti+2S)modT...
将这些人放在一个环中,可以发现两两之间是没有交,那么怎么样可以更简单的计算答案,把环分成 gcd(S,T) ,环长就是 Tgcd(S,T) ,一个人可以取的石子就是与下一个人在所在环上距离。
证明显然,其实是不会,感性愉悦下就好。
代码:
#include <cstdio>
#include <map>
#include <string.h>
#include <algorithm>
#include <cmath>
using namespace std;
int rd() {
int x = 0; char c = getchar();
while (c > '9' || c < '0') c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x;
}
void wt(int x) {
if (x >= 10) wt(x / 10);
putchar(x % 10 + 48);
}
void exgcd(int a, int b, int &x, int &y) {
if (!b) { x = 1, y = 0; return; }
exgcd(b, a % b, x, y);
int t = x;
x = y, y = t - a / b * y;
}
const int N = 2e5 + 10;
struct Pt{
int bel, pos, id;
Pt(int a = 0, int b = 0, int c = 0) : bel(a), pos(b), id(c) {}
bool operator < (const Pt &t) const {
return bel < t.bel || (bel == t.bel && pos < t.pos);
}
} c[N];
map <int, int> mp;
int T, n, a[N], ans[N], S, cnt;
int main() {
T = rd(), n = rd();
for (int i = 1; i <= n; i ++) a[i] = rd(), S = (S + a[i]) % T;
int t = -a[1];
for (int i = 1; i <= n; i ++) {
t = (t + a[i]) % T;
if (!mp[t]) mp[t] = i;
}
int g = __gcd(S, T), len = T / g, step = S / g, inv, y;
//printf("%d %d %d\n", S, T, g);
t = -a[1], exgcd(step, len, inv, y);
inv = (inv + len) % len;
for (int i = 1; i <= n; i ++) {
t = (t + a[i]) % T;
if (mp[t] == i) c[++cnt] = Pt(t % g, 1ll * (t / g) * inv % len, i);
}
sort(c + 1, c + cnt + 1);
for (int i = 1; i <= cnt; i ++) {
int j = i;
while (j < cnt && c[j + 1].bel == c[i].bel) j ++;
//printf("%d\n", j);
for (int k = i; k < j; k ++) ans[c[k].id] += c[k + 1].pos - c[k].pos;
ans[c[j].id] += c[i].pos + len - c[j].pos;
i = j;
}
for (int i = 1; i <= n; i ++) wt(ans[i]), putchar(32); puts("");
return 0;
}