BZOJ4373 算数天才⑨和等差数列 学习zkw线段树

膜一下zkw神犇  另外fread真的快。。

题目:

http://www.lydsy.com/JudgeOnline/problem.php?id=4373

大意:给你一个n的数列,每次查询一段区间[l,r]内的数字排序后是否构成公差为k的等差数列。并且有修改操作。

思路:难点就在于判断是否构成等差数列。我们可以用平方和的方式,Hash一下,然后和等差数列平方和公式算出来的值进行比对,如果不一致那么说明其中有数不一样,即不构成等差数列。然后就是线段树一通乱搞(确信

这个是我看下来觉得写的最明白的zkw线段树的介绍(教程)。当然了也可以去看zkw本尊的ppt《统计的力量》。

点击打开链接


#include<iostream>
#include<string>
#include<cstring>
#include<stdlib.h>
#include<cstdio>
#include<stdio.h>
#include<set>
#include<map>
#include<deque>
#include<stack>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<list>
#include<fstream>
#include <bitset>
using namespace std;
typedef long long ll;
const int maxn = 300010;
const int INF = 0x3f3f3f3f;
int bas = 1;
struct seg {
	int mn;ll sum2;
}t[maxn << 2];
char * cp=(char *)malloc(20000000);
void in(int &x){
    while(*cp<'0'||*cp>'9')++cp;
    for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}
inline void pushup(int n)
{
	t[n].mn = min(t[n << 1].mn, t[n << 1 | 1].mn);
	t[n].sum2 = t[n << 1].sum2 + t[n << 1 | 1].sum2;
}
void build(int n)
{
	while (bas < (n + 2))//底层有几个节点
		bas <<= 1;
	for (int i = bas + 1;i <= bas + n;i++)
	{
		in(t[i].mn);t[i].sum2 = t[i].mn *t[i].mn * 6;//这里让平方和乘以6因为公式里有除以6,为了避免除法和逆元什么的乱七八糟的东西
	}
	for (int i = bas - 1;i > 0;i--)
		pushup(i);
}
void update(int n, int val)
{
	n += bas;
	t[n].mn = t[n].sum2 = val;
	t[n].sum2 = t[n].sum2 * t[n].sum2 * 6;
	for (n >>= 1;n > 0;n >>= 1)    //一直更新到根节点
		pushup(n);
}
ll qsum(int l, int r)
{
	ll ans = 0;
	for (l += bas - 1, r += bas + 1;l^r ^ 1;l >>= 1, r >>= 1)
	{
		if ((~l) & 1)ans = ans + t[l ^ 1].sum2;    //如果左边的节点是左儿子则把右儿子加入sum
		if (r & 1)ans =ans + t[r ^ 1].sum2;        //如果右边的节点是右儿子则把左儿子加入sum
	}
	return ans;
}
int qmin(int l, int r)
{
	int ans = INF;
	for (l += bas - 1, r += bas + 1;l^r ^ 1;l >>= 1, r >>= 1)
	{
		if ((~l) & 1)ans = min(ans, t[l ^ 1].mn); 
		if (r & 1)ans = min(ans, t[r ^ 1].mn);
	}
	return ans;
}
int main()
{
	int n, m, opt, l, r, k, cnt = 0;
        fread(cp,1,20000000,stdin);
	in(n);in(m);
	build(n);
	while (m--)
	{
		in(opt);in(l);in(r);
		l ^= cnt, r ^= cnt;
		if (opt == 2)
		{
			in(k);
			k ^= cnt;
			int sum = qsum(l, r), mm = qmin(l, r), len = r- l;
			if (sum == mm*mm*(len + 1) * 6 + len*(len + 1)*mm*k * 6 + len*(len + 1)*(len << 1 | 1)*k*k)
			{
				cnt++;printf("Yes\n");
			}
			else printf("No\n");
		}
		else if (opt == 1)
		{
			update(l, r);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值