牛客练习赛16 D.k进制 思维

题意:对于k进制数x,定义d(x)为x个数位的和的k进制表示.若结果超过一位,则继续执行.
7进制下 d(3504)=d(3+5+0+4)=d(15)=d(1+5)=d(6)=6
若d(x)=b,则称x为幸运的.
2<=k<=1e9, 0<=b<k , 1<=n<=1e5.
给出k进制下的n个数位[a[1],a[2]...a[n]],以及b,问其中有多少个子串是幸运的?


因为每个x的最终价值都是1<=val<k的.那么知道[i,j-1]的价值x,可以容易知道[i+1,j]的价值y.
val=x+a[j] val最大为2*k-2 若val>=k 则进位,十位为1,个位为val-k.最后结果:1+val-k<=k-1.
直接枚举计算O(n^2) TLE.

(a[L]+a[L+1]+..a[R]) 每次若有两个数相加超过k,则会用一个1和余k部分来代替.
那么d[L,R]等价于(a[L]+a[L+1]+.a[R]) %(k-1) 的值.(若最后余数为0,则结果为k-1)


现在求出前缀p[i]%(k-1)的值.
枚举右端点r,要想知道有多少个左端点L,满足d[L,R]==b.
那么 d[L-1]+b = d[r] (mod k-1) .两边同时减去b.
d[L-1] = d[r]-b (modk-1)

注意当b==0的时候 只能取a[i]==0的区间.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll k,b,n,a[N],pre[N];
map<ll,ll> cnt;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>k>>b>>n;
	ll res=0,z=0,t=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		pre[i]=(pre[i-1]+a[i])%(k-1);
		if(a[i]==0)
			t++,z+=t;
		else
			t=0;
	}
	if(b==0)
	{
		cout<<z<<'\n';
		return 0;
	}
	cnt[0]++;
	for(int i=1;i<=n;i++)
	{
		ll val=(pre[i]-b+k-1)%(k-1);
		res+=cnt[val];
		cnt[pre[i]]++;
	}
	if(b==k-1)
		res-=z;
	cout<<res<<'\n';
	return 0;
}


猜你喜欢

转载自blog.csdn.net/noone0/article/details/80173259
今日推荐