[ABC367D] Pedometer-xzy巨佬简洁做法

[ABC367D] Pedometer-xzy巨佬简洁做法

https://www.luogu.com/article/n64n78cs

对照巨佬的代码进一步理解

//徐知鱼
#include <bits/stdc++.h>
using namespace std;
inline int read() {
	int x = 0, f = 1;
	char ch = getchar();
	while(!isdigit(ch)) {
		if(ch == '-') f = -1;
		ch = getchar();
	}
	while(isdigit(ch)) {
		x =  x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}
const int N = 400010,M=1000010;
int n, m, a[N], s[N], t[M];
long long ans;
int main() {
	#ifdef LOCAL
	freopen("1.txt","r",stdin);
	#endif
	#ifndef LOCAL
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	#endif
	n = read(), m = read();
	for(int i = 1; i <= n; ++i) a[i] = a[i + n] = read();//复制一遍
	for(int i = 1; i <= n * 2-1; ++i) s[i] = (s[i - 1] + a[i]) % m;
    //破环为链后的前缀和,s[2n]表示从2n~2n+1,而1一直到n后,n+1就是1了
    //所以2n+1最后回到1,就是1->2->3...n-1->n->1'->2'->3'...(n-1)'->n'->1''
	for(int i = 1; i <= n * 2-1; ++i) {
		if(i > n) --t[s[i - n]];
		ans += t[s[i]];
		if(i <= n) ++t[s[i]];
	}
    /*
    我们先优先考虑i<=n的情况。
    此时,每次加入,实际上就是看和以i+1结尾的同余的位置,我们就处理完了从小编号到大编号的部分。
    之后,到了1',删去了s[1],那么就是只有从2~n到1了,就算是处理大编号到小编号的情况。
    事实上,到n',就没有贡献了,但是之后也没有贡献,图方便就用2n了。
    
    这里循环的顺序也很有考究。
    就是每次插入s[i]是在计算之后,就保证不会自己到自己
    而当i=n时,计算的恰好是1~n,s<t 的情况。
    之后当i=n+1时,会删去s[1],这样就是2~n过来了。
    之后同理,变成2~n以及1'
    可以保证答案了。
    因此,可以枚举到2n-1,这样是n到(n-1)',枚举到2n,此时t数组为空,计算也无妨。
    */
	cout << ans << '\n';
	return 0;
}
posted @ 2024-08-19 15:37  wscqwq  阅读(15)  评论(0编辑  收藏  举报