4262: Sum

4262: Sum

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 66   Solved: 29
[ Submit][ Status][ Discuss]

Description

Input

第一行一个数 t,表示询问组数。
第一行一个数 t,表示询问组数。
接下来 t 行,每行四个数 l_1, r_1, l_2, r_2。

Output

一共 t 行,每行一个数 Sum。

Sample Input

4
1 3 5 7
2 4 6 8
1 1 9 9
9 9 1 1

Sample Output

9322587654
9025304064
1065645568
0

HINT

1<=t<=40000,1<=L1<R1<=10^5,1<=L2<=R2<=10^5


Source

[ Submit][ Status][ Discuss]

询问可以看做每次固定一右端点,左端点不断移动进行统计
离线询问,按右端点排序,定义一些状态

也就是说,,如果能快速更新sum与val数组,询问就可以通过区间减法完成了
这里只讨论min的做法,max类似。。。
考虑右端点不断右移,对于每个位置i,预处理L[i]为i左边第一个小于i的数的位置
那么对于j∈(L[i],i],val[j][i] = A[i],再往左val数组的值不变
也就是说除了这一段用于增加sum数组的val改变,再往左就是再加一遍上一个版本的val
因此需要维护两个关键字,借助矩阵乘法转移,用线段树维护
具体做法是维护四个关键字a,b,c,d,让val和sum转移如下
val = a*val' + b*len    sum = sum' + c*val' + d*len   (带'的变量表示上一版本)
转移写成下三角矩阵(因为这样转移矩阵相乘后还是下三角)
矩阵乘法满足结合律,将左边两个手算出来,a,b,c,d的转移就能写成公式
就避免了常数27。。(带此常数会TLE的= =)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E5 + 10;
const int T = 4;
typedef long long LL;
const LL mo = 1000000000;

struct Query{
	int l,r,g,num,typ; Query(){}
	Query(int l,int r,int g,int num,int typ): l(l),r(r),g(g),num(num),typ(typ){}
	bool operator < (const Query &b) const
	{
		return g < b.g;
	}
}Q[maxn];

int m,N,tot,f[maxn],L[maxn][2];
LL ans[maxn],len[maxn*T],val[maxn*T],sum[maxn*T],a[maxn*T],b[maxn*T],c[maxn*T],d[maxn*T];

stack <int> s;

int Add(LL x,LL y) {return (x + y + mo)%mo;}
int Mul(LL x,LL y) {return x*y%mo;}

void Build(int o,int l,int r)
{
	len[o] = r - l + 1;
	a[o] = 1; b[o] = c[o] = d[o] = 0;
	sum[o] = val[o] = 0;
	if (l == r) return;
	int mid = (l + r) >> 1;
	Build(o<<1,l,mid);
	Build(o<<1|1,mid+1,r);
}

void Work(int x,int y)
{
	LL A,B,C,D;
	B = b[x] + a[x]*b[y];
	A = a[x]*a[y];
	D = d[x] + b[y]*c[x] + d[y];
	C = a[y]*c[x] + c[y];
	a[y] = A; b[y] = B;
	c[y] = C; d[y] = D;
}

void pushdown(int o,int l,int r)
{
	if (a[o] == 1 && !b[o] && !c[o] && !d[o]) return;
	LL VAL = a[o]*val[o] + b[o]*len[o];
	LL SUM = sum[o] + c[o]*val[o] + d[o]*len[o];
	val[o] = VAL; sum[o] = SUM;
	if (l == r) {a[o] = 1; b[o] = c[o] = d[o] = 0; return;}
	Work(o,o<<1); Work(o,o<<1|1);
	a[o] = 1; b[o] = c[o] = d[o] = 0;
}

void Modify(int o,int l,int r,int ml,int mr,int x)
{
	if (ml <= l && r <= mr)
	{
		a[0] = 0; b[0] = x;
		c[0] = 0; d[0] = 0;
		Work(0,o);
		pushdown(o,l,r); 
		return;
	}
	pushdown(o,l,r);
	int mid = (l + r) >> 1;
	if (ml <= mid) Modify(o<<1,l,mid,ml,mr,x); else pushdown(o<<1,l,mid);
	if (mr > mid) Modify(o<<1|1,mid+1,r,ml,mr,x); else pushdown(o<<1|1,mid+1,r);
	val[o] = val[o<<1] + val[o<<1|1];
	sum[o] = sum[o<<1] + sum[o<<1|1];
}

LL query(int o,int l,int r,int ql,int qr)
{
	pushdown(o,l,r);
	if (ql <= l && r <= qr) return sum[o];
	int mid = (l + r) >> 1; LL ret = 0;
	if (ql <= mid) ret += query(o<<1,l,mid,ql,qr);
	if (qr > mid) ret += query(o<<1|1,mid+1,r,ql,qr);
	return ret;
}

void Pre_Work()
{
	for (int i = 1,A = 1023,B = 1025; i <= N; i++)
		f[i] = A^B,A = Mul(A,1023),B = Mul(B,1025);
	for (int i = N; i; i--)
	{
		while (!s.empty() && f[s.top()] < f[i])
			L[s.top()][0] = i,s.pop();
		s.push(i);
	}
	while (!s.empty()) L[s.top()][0] = 0,s.pop();
	for (int i = N; i; i--)
	{
		while (!s.empty() && f[s.top()] > f[i])
			L[s.top()][1] = i,s.pop();
		s.push(i);
	}
	while (!s.empty()) L[s.top()][1] = 0,s.pop();
}

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

int main()
{
	//freopen("4262.in","r",stdin);
	//freopen("4262.out","w",stdout);
	
	m = getint();
	for (int i = 1; i <= m; i++)
	{
		int l1,r1,l2,r2;
		l1 = getint(); r1 = getint();
		l2 = getint(); r2 = getint();
		if (l2 > 1) Q[++tot] = Query(l1,r1,l2-1,i,-1);
		Q[++tot] = Query(l1,r1,r2,i,1);
		N = max(N,r2); N = max(N,r1);
	}
	Pre_Work(); 
	
	sort(Q + 1,Q + tot + 1);
	int now = 0; Build(1,1,N);
	for (int i = 1; i <= tot; i++)
	{
		while (now < Q[i].g)
		{
			++now;
			Modify(1,1,N,L[now][0] + 1,now,f[now]);
			a[0] = 1; b[0] = 0;
			c[0] = 1; d[0] = 0;
			Work(0,1);
		}
		ans[Q[i].num] += 1LL*Q[i].typ*query(1,1,N,Q[i].l,Q[i].r);
	}
	now = 0; Build(1,1,N);
	for (int i = 1; i <= tot; i++)
	{
		while (now < Q[i].g)
		{
			++now;
			Modify(1,1,N,L[now][1] + 1,now,f[now]);
			a[0] = 1; b[0] = 0;
			c[0] = 1; d[0] = 0;
			Work(0,1);
		}
		ans[Q[i].num] -= 1LL*Q[i].typ*query(1,1,N,Q[i].l,Q[i].r);
	}
	for (int i = 1; i <= m; i++) printf("%lld\n",ans[i]);
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值