bzoj5308[Zjoi2018]胖(线段树,二分,st表)

Description
Cedyks是九条可怜的好朋友(可能这场比赛公开以后就不是了),也是这题的主人公。
Cedyks是一个富有的男孩子。他住在著名的ThePLace(宫殿)中。
Cedyks是一个努力的男孩子。他每天都做着不一样的题来锻炼他的The SaLt(灵魂)。
这天,他打算在他的宫殿外围修筑一道城墙,城墙上有n座瞭望塔。
你可以把城墙看做一条线段,瞭望塔是线段上的n个点,其中1和n分别为城墙的两个端点。
其中第i座瞭望塔和第i+1座瞭望塔的距离为wi,他们之间的道路是双向的。
城墙很快就修建好了,现在Cedyks开始计划修筑他的宫殿到城墙的道路。
因为这题的题目名称,
Cedyks打算用他的宫殿到每一个瞭望塔的最短道路之和来衡量一个修建计划。
现在Cedyks手上有m个设计方案,第k个设计方案会在宫殿和瞭望塔之间修建Tk条双向道路,
第i条道路连接着瞭望塔ai,长度为Li。
计算到每一个瞭望塔的最短路之和是一个繁重的工程,本来Cedyks想用广为流传的SPFA算法
来求解,但是因为他的butter(缓冲区)实在是太小了,他只能转而用原始的贝尔福特曼算法
来计算,算法的流程大概如下:
1:定义宫殿是0号点,第i个瞭望塔是i号点,双向边(ui,vi,Li)为一条连接ui和vi的双向道路。
令d为距离数组,最开始d0=0,di=10^18(i∈[1,n])。
2:令辅助数组c=d。依次对于每一条边(ui,vi,wi)进行增广,
cui=min(cui,dvi+wi),
cvi=min(cvi,dui+wi)。
3:令t为c和d中不一样的位置个数,即令S={i|ci!=di},则t=S。若t=0,说明d
就是最终的最短路,算法结束。否则令d=c,回到第二步。
因为需要计算的设计方案实在是太多了,所以Cedyks雇佣了一些人来帮他进行计算。
为了避免这些人用捏造出来的数据偷懒,他定义一个设计方案的校验值为在这个方案
上运行贝尔福特曼算法每一次进入第三步t的和。他会让好几个雇佣来的人计算同样
的设计方案,并比对每一个人给出的校验值。
你是Cedyks雇佣来的苦力之一,聪明的你发现在这个情形下计算最短路的长度的和
是一件非常简单的事情。但是寄人篱下不得不低头,你不得不再计算出每一个方案
的校验值来交差。
Input
第一行输入两个整数n,m,表示瞭望塔个数和设计方案个数。
接下来一行n-1个数wi,表示瞭望塔i和i+1之间道路的长度。
接下来m行,每行描述一个设计方案。
第一个整数K表示设计方案中的道路数量,
接下来K个数对(ai,Li)为一条宫殿到瞭望塔的边。
1 <= wi, li <= 109, 1 <= ∑ K <= 2 × 10^5
N,M<=2*10^5
Output
对于每一个设计方案,输出一行一个整数表示校验值。
Sample Input
5 5
2 3 1 4
1 2 2
2 1 1 4 10
3 1 1 3 1 5 1
3 1 10 2 100 5 1
5 1 1 2 1 3 1 4 1 5 1
Sample Output
5
8
5
8
5


s o l u t i o n solution
省选两天里面最水的一道题
考虑到一个关键点能有贡献的答案必然是一段连续的区间
就可以考虑二分了。

下面讨论一下二分右端点的过程,左端点类似
我们先把其他的点查分到左右端点的值计算一下
对于一个 m i d mid ,我只要考虑 [ p o s , m i d ] [pos,mid] ( p o s pos 为关键点的位置)区间内的关键点查分到左边的值以及 [ m i d , n ] [mid,n] 内的关键点查分到右边的值

还要考虑相等的时候
细节比较多

刚刚那个过程显然可以线段树做。(普通线段树可能被卡常,要zkw线段树)
复杂度 O ( n l o g 2 n ) O(nlog^2n)

实际上我们发现我们在做的时候时不用修改的,我们用 s t st 表就行了,一开始 s b sb 了,以为建立 s t st 表的总复杂度是 m n l o g n mnlogn 的,后来意识到只要对关键点建立就行了

复杂度 O ( n l o g n ) O(nlogn)
s t st 表代码没写,和线段树差别不大

线段树代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
#define ls rt<<1
#define rs rt<<1|1
#define fr first
#define se second
int rd()
{
    int num = 0;char c = getchar();bool flag = true;
    while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
    while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
    if(flag) return num;else return -num;
}
const ll INF = 1e17;
inline ll min(ll a,ll b){return a<b?a:b;}
int n,m,k,flen;
int v[201000];
struct point{int pl,v;}a[201000];
ll sum[201000],_sum[201000];
//sum表示前缀和,_sum表示后缀和
//0表示从前开始的查分,1表示从后
namespace tree
{
	ll mn[2][801000];
	void change(int x,ll a,ll b)
	{
		x += flen;mn[0][x] = a;mn[1][x] = b;
		while(x)
		    x>>=1,
		    mn[0][x] = min(mn[0][x<<1],mn[0][x<<1|1]),
		    mn[1][x]     = min(mn[1][x<<1],mn[1][x<<1|1]);
	}
	ll query(int l,int r,int op)
	{
		if(l<1) l = 1;
		if(r>n) r = n;
		if(l>r) return INF;
		ll ans = INF;
		for(l+=flen-1,r+=flen+1;l^r^1;l>>=1,r>>=1)
		{
			if(~l&1) ans = min(ans,mn[op][l^1]);
			if(r&1)  ans = min(ans,mn[op][r^1]);
		}
		return ans;
	}
}using namespace tree;
bool mycmp(point x,point y){return x.pl < y.pl;}
void work()
{
	rep(i,1,k) tree::change(a[i].pl,1ll*a[i].v-sum[a[i].pl],1ll*a[i].v-_sum[a[i].pl]);
	ll ans = 0;
	rep(i,1,k) 
	{
	    //考虑第i个关键点向右的贡献,相同算贡献 
		int l = a[i].pl,r = n+1;
		while(l+1<r)
		{
			int mid = l+r>>1;
			ll len = sum[mid] - sum[a[i].pl] + a[i].v,tp;//距离
			//先处理端点 
			tp = tree::query(2*mid-a[i].pl,2*mid-a[i].pl,1);
			if(tp + _sum[mid] < len){r = mid;continue;}
			//一般情况 
			tp = (a[i].pl+1<=mid)?tree::query(a[i].pl+1,mid,0):INF;
			if(tp + sum[mid] <= len) {r = mid;continue;}
			len -= _sum[mid];//查分到右端 
			tp = (mid<=2*mid-a[i].pl-1)?tree::query(mid,2*mid-a[i].pl-1,1):INF;
			if(len >= tp) {r = mid;continue;}
			l = mid;
		}
		ans += l-a[i].pl+1;
		//考虑向左的贡献,相同不算贡献
		l = 0,r = a[i].pl;
		while(l+1<r)
		{
			int mid = l+r>>1;
			ll len = sum[a[i].pl] - sum[mid] + a[i].v,tp;
			//先处理端点
			tp = tree::query(2*mid-a[i].pl,2*mid-a[i].pl,0);
			if(tp + sum[mid] <= len){l = mid;continue;}
			//一般情况 
			tp = (mid<=a[i].pl-1)?tree::query(mid,a[i].pl-1,1):INF;
			if(tp + _sum[mid] <= len) {l = mid;continue;}
			len -= sum[mid];//查分到左侧
			tp = (2*mid-a[i].pl+1<=mid)?tree::query(2*mid-a[i].pl+1,mid,0):INF;
			if(len >= tp) {l = mid;continue;}
			r = mid;
		}
		ans += a[i].pl - r;
	}
	rep(i,1,k) change(a[i].pl,INF,INF);
	printf("%lld\n",ans);
}
int main()
{
	n = rd();m = rd();flen = 1;while(flen < n) flen <<= 1;
	rep(i,1,4*n) mn[0][i] = mn[1][i] = INF;
	rep(i,1,n-1) v[i] = rd(),sum[i+1] = sum[i] + v[i];
	repp(i,n-1,1) _sum[i] = _sum[i+1] + v[i];
	rep(i,1,m)
	{
		k = rd();
		rep(j,1,k) a[j].pl = rd(),a[j].v = rd();
		work();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/85135997