Problem 2 交错和查询

【题目描述】

 

无限循环数字串S由长度为n的循环节s构成。设s为12345(n=5),则数字串S为123451234512345…

设Si为S的第i位数字,在上面的例子中,S1=1,S2=2,S6=1。

设S的一个子串S[l,r]的交错和为sum(l,r):

sum(l,r) = Sl - S1+1 + Sl+2- Sl+3 + … + (-1)r-lSr

如sum(2,7) = 2 - 3 + 4 - 5 + 1 - 2 = -3

 

现给定循环节s,要求支持两种操作:

1pos digit:

修改循环节s上的某一位,即将spos改为digit。

2 l r:

       求S[l,r]内所有子串的交错和的和,即

       输出ans对109+7的模。

 

【输入格式】

 

第一行一个整数n,表示循环节s的长度。

第二行一个长度为n的数字串,表示循环节s。

第三行一个整数m,表示操作次数。

以下m行,每行3个整数。

    若第一个数为1,表示是修改操作1 pos digit。

    若第一个数为2,表示是询问操作2 l r。

 

【输出格式】

 

对于每个询问操作输出一行,表示答案。

 

【样例输入】

5

12345

5

2 1 5

2 6 10

1 3 5

2 1 5

2 1 6

【样例输出】

19

19

25

36

【数据范围】

   

    对于10%的数据点,n, m <= 50;

    对于20%的数据点,n, m <=1000;

    对于40%的数据点,1 <= l<= r <= n;

    对于100%的数据点,n, m <=200000;1 <= l <= r <= 1018;1 <= pos <= n;0 <= digit <= 9;

通过分析或者带入样例可发现答案为s[i]*(r - i+1), 其中l<=i<=r;

通过拆式子可化为s[i]*r-s[i]*i+s[i],对于40分我们直接线段树。

对于100分我们把序列拆成3段,分别为[l,an],[an + 1, bn],[bn + 1, r],中间那个就是个等差数列。

说起来真他妈简单(这傻逼被这题卡了6个半小时)

注意MOD,其中有的是longlong*int,所以一定要注意(这傻逼后面2小时一直都在查,最后发现longlong*int爆了)。如果小数据能过大数据过不去八成就是这个问题。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 400005;
const int MOD = 1000000007;
char c[MAXN];
int n, i, j, k, m, a[MAXN << 1], ty, sum[MAXN << 2][4], tot1, tot2, ans;
long long l, r;
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
	res = res * 10 + c - 48;
	return res;
}
inline long long get1()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	long long res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
	res = res * 10 + c - 48;
	return res;
}
inline int ksm(int x, int y)
{
	if (!y) return 1;
	int tmd = ksm(x, y >> 1);
	tmd = (long long)tmd * tmd % MOD;
	if (y & 1) tmd = tmd * (long long)x % MOD;
	return tmd;
}
inline void change(int &x)
{
	if (x >= MOD) x -= MOD;
}
inline void updata(int k)
{
	sum[k][0] = sum[k << 1][0] + sum[k << 1 | 1][0]; change(sum[k][0]);
	sum[k][1] = sum[k << 1][1] + sum[k << 1 | 1][1]; change(sum[k][1]);
	sum[k][2] = sum[k << 1][2] + sum[k << 1 | 1][2]; change(sum[k][2]);
	sum[k][3] = sum[k << 1][3] + sum[k << 1 | 1][3]; change(sum[k][3]);
}
inline void maketree(int k, int p, int q)
{
	if (p == q)
	{
		if (p & 1) sum[k][0] = a[p] * (long long)p % MOD, sum[k][1] = a[p];
		else sum[k][2] = a[p] * (long long)p % MOD, sum[k][3] = a[p];
		return;
	}
	int mid = (p + q) >> 1;
	maketree(k << 1, p, mid);
	maketree(k << 1 | 1, mid + 1, q);
	updata(k);
}
inline void change(int k, int p, int q, int w, int w1)
{
	if (p == q)
	{
		if (p & 1) sum[k][0] = p * (long long)w1 % MOD, sum[k][1] = w1;
		else sum[k][2] = p * (long long)w1 % MOD, sum[k][3] = w1;
		return;
	}
	int mid = (p + q) >> 1;
	if (mid >= w) change(k << 1, p, mid, w, w1);
	if (mid < w) change(k << 1 | 1, mid + 1, q, w, w1);
	updata(k);
}
inline void find(int k, int p, int q, int l, int r, int w)
{
	if (p >= l && q <= r)
	{
		if (w) tot1 += sum[k][0], tot2 += sum[k][1];
		else tot1 += sum[k][2], tot2 += sum[k][3];
		change(tot1); change(tot2);
		return;
	}
	int mid = (p + q) >> 1;
	if (mid >= l) find(k << 1, p, mid, l, r, w);
	if (mid < r) find(k << 1 | 1, mid + 1, q, l, r, w);
}
int main()
{
	freopen("sum.in", "r", stdin);
	freopen("sum.out", "w", stdout);
	cin >> n;
	scanf("%s", c + 1);
	for(i = 1; i <= n; i ++)
		a[i] = c[i] - 48;
	maketree(1, 1, n);
	cin >> m;
	while (m --)
	{
		ty = get();
		if (ty == 1)
		{
			int x = get(), y = get();
			change(1, 1, n, x, y);
		}
		else
		{
			l = get1(); r = get1();
			long long blockl = l / n + (l % n != 0), blockr = r / n + (r % n != 0);
			int ll = (l - 1) % n + 1;
			int rr = (r - 1) % n + 1;
			long long t = (blockl - 1) * n;
			ans = 0;
			if (blockl == blockr)
			{
				tot1 = tot2 = 0;
				find(1, 1, n, ll, rr, ll & 1);
				int part1 = r % MOD * tot2 % MOD;
				int part2 = tot1 + t % MOD * tot2 % MOD;
				change(part2);
				int part3 = tot2;
				ans = part1 - part2 + part3;
				if (ans < 0) ans += MOD;
				change(ans);
				printf("%d\n", ans);
				continue;
			}
			tot1 = tot2 = 0;
			find(1, 1, n, ll, n, ll & 1);
			int part1 = r % MOD * tot2 % MOD;
			int part2 = tot1 + t % MOD * tot2 % MOD;
			change(part2);
			int part3 = tot2;
			int ans1 = part1 - part2 + part3;
			change(ans1);
			if (ans1 < 0) ans1 += MOD;
			
			long long fp = (r - l - ((r - 1) % n + 1)) & 1;
			tot1 = tot2 = 0;
			t = (blockr - 1) * n;
			find(1, 1, n, 1, rr, fp);
			part1 = r % MOD * tot2 % MOD;
			part2 = tot1 + t % MOD * tot2 % MOD;
			change(part2);
			part3 = tot2;
			int ans2 = part1 - part2 + part3;
			change(ans2);
			if (ans2 < 0) ans2 += MOD;
			ans = ans1 + ans2;
			change(ans);
			
			t = (blockr - blockl - 1) * n;
			tot1 = tot2 = 0;
			find(1, 1, n, 1, n, (n - ll) & 1);
			int pre1 = tot1, pre2 = tot2;
			if ((blockr - blockl - 1) & 1)
			{
				t = (blockr - 2) * n;
				part1 = r % MOD * tot2 % MOD;
				part2 = (long long)tot1 + t % MOD * tot2 % MOD;
				change(part2);
				part3 = tot2;
				ans1 = part1 - part2 + part3;
				change(ans1);
				ans += ans1;
				change(ans);
				if (ans < 0) ans += MOD;
			}
			tot1 = tot2 = 0;
			if (n & 1) find(1, 1, n, 1, n, (n - ll + 1) & 1);
			else find(1, 1, n, 1, n, (n - ll) & 1);
			tot1 = ((long long)pre1 + (long long)tot2 * n % MOD) % MOD + tot1;
			change(tot1);
			tot2 += pre2; change(tot2);
			t = blockl * n;
			part1 = r % MOD * (long long)tot2 % MOD;
			part2 = (long long)tot1 + t % MOD * (long long)tot2 % MOD;
			change(part2);
			part3 = tot2;
			long long tim = (blockr - blockl - 1) >> 1;
			if (!tim) {printf("%d\n", ans); continue;}
			part1 = tim % MOD * part1 % MOD;
			part3 = tim % MOD * part3 % MOD;
			int d = 2 * n * (long long)tot2 % MOD, mo = (tim - 1) % MOD * (long long)d % MOD + part2;
			change(mo);
			part2 = tim % MOD * (part2 + mo) % MOD * (long long)ksm(2, MOD - 2) % MOD;
			ans1 = part1 - part2 + part3;
			change(ans1);
			ans += ans1;
			change(ans);
			if (ans < 0) ans += MOD;
			printf("%d\n", ans);
		}
	}
	fclose(stdin); fclose(stdout);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值