1. 程式人生 > >[bzoj4636]蒟蒻的數列_線段樹

[bzoj4636]蒟蒻的數列_線段樹

fread cstring else if amp pre lin esp query int

蒟蒻的數列 bzoj-4636

題目大意:給定一個序列,初始均為0。n次操作:每次講一段區間中小於k的數都變成k。操作的最後詢問全局和。

註釋:$1\le n\le 4\cdot 10^4$。


想法:那個操作就是一個不好好說話的操作,說白了就是對區間的每一個數取max

然後我們對於那個序列建立分治線段樹。每個操作我都把它掛在對應的log的點上。

n個操作都執行完了之後我們從1號節點深搜,更新答案即可。

最後,附上醜陋的代碼... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define inf 1e9 
#define N 40010 
using namespace std;
typedef long long ll;
ll maxn[N*40],ans=0;
int ls[N*40],rs[N*40],cnt;
int root;
inline char nc()
{
	static char *p1,*p2,buf[100000];
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
ll read()
{
	ll x=0; char c=nc();
	while(!isdigit(c)) c=nc();
	while(isdigit(c)) x=(x<<3)+(x<<1)+c-‘0‘,c=nc();
	return x;
}
void update(int x,int y,ll val,int l,int r,int &pos)
{
	if(!pos) pos=++cnt;
	if(x==l&&r==y)
	{
		maxn[pos]=max(maxn[pos],val);
		return;
	}
	int mid=(l+r)>>1;
	if(y<=mid) update(x,y,val,l,mid,ls[pos]);
	else if(mid<x) update(x,y,val,mid+1,r,rs[pos]);
	else
	{
		update(x,mid,val,l,mid,ls[pos]);
		update(mid+1,y,val,mid+1,r,rs[pos]);
	}
	// if(x<=mid) update(x,y,val,l,mid,ls[pos]);
	// if(mid<y) update(x,y,val,mid+1,r,rs[pos]);
}
void query(ll val,int l,int r,int pos)
{
	if(!pos) return;
	maxn[pos]=max(maxn[pos],val);
	if(!ls[pos]&&!rs[pos])
	{
		ans+=maxn[pos]*(r-l+1);
		return;
	}
	int mid=(l+r)>>1;
	query(maxn[pos],l,mid,ls[pos]);
	query(maxn[pos],mid+1,r,rs[pos]);
	if(!ls[pos]) ans+=maxn[pos]*(mid-l+1);
	if(!rs[pos]) ans+=maxn[pos]*(r-mid);
}
int main()
{
	int n=read();
	int x,y; ll val;
	for(int i=1;i<=n;++i)
	{
		x=read(),y=read(),val=read();
		if(x==y) continue;
		update(x,y-1,val,1,inf,root);
	}
	query(0,1,inf,root);
	printf("%lld\n",ans);
	return 0;
}

小結:get新技能:分治線段樹。

[bzoj4636]蒟蒻的數列_線段樹