zoukankan      html  css  js  c++  java
  • Luogu3722 影魔

    真的比较神的一个题

    Description

    link

    给定一个排列,如果点对 ((i,j)) 满足:

    (1.) (i ightarrow j) 中(两端不含)元素均小于 (p_i)(p_j) 则产生 (a_1) 点贡献

    (2.) (i ightarrow j)中(两端不含)元素的最大值如果在(p_i)(p_j) 之间则产生 (a_2) 点贡献

    Solution

    首先发现要贡献法……想不到就没救啦

    分开考虑两种值

    第一种我们枚举一个点当做区间最大值,找到左右离得最近的比它大的两个数,这里就产生了贡献

    然后第二种

    对于每个点(i)

    (l ightarrow i)(r)

    (i ightarrow r)(l)

    分别是满足条件的

    然后我们看怎么求

    左右的两个大数是可以单调栈求的

    for(int i=1;i<=n+1;++i) 
    {
    	while(top&&a[st[top]]<a[i]) r[st[top]]=i,top--;
    	l[i]=st[top]; st[++top]=i;
    }
    

    然后我们统计答案(蒟蒻不会这里的统计答案)

    其实就是个区间加区间求和的线段树,不知道为啥没想到

    还搞了(std) 才看懂……

    Code

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    namespace yspm{
    	inline int read()
    	{
    		int res=0,f=1; char k;
    		while(!isdigit(k=getchar())) if(k=='-') f=-1;
    		while(isdigit(k)) res=res*10+k-'0',k=getchar();
    		return res*f;
    	}
    	const int N=2e5+10;
    	struct tree{
    		int l,r,sum,add;
    		#define l(p) t[p].l
    		#define r(p) t[p].r
    		#define sum(p) t[p].sum
    		#define add(p) t[p].add
    	}t[N<<5];
    	int rt[N],tot,l[N],r[N],st[N],top,n,m,p1,p2,a[N];
    	inline void build(int p,int l,int r)
    	{
    		l(p)=l; r(p)=r; if(l==r) return ;
    		int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r);
    		return ;
    	}
    	inline void spread(int p)
    	{
    		if(add(p))
    		{
    			sum(p<<1)+=add(p)*(r(p<<1)-l(p<<1)+1);
    			sum(p<<1|1)+=add(p)*(r(p<<1|1)-l(p<<1|1)+1);
    			add(p<<1)+=add(p); add(p<<1|1)+=add(p);
    		}return add(p)=0,void();
    	}
    	inline void push_up(int p){return sum(p)=sum(p<<1)+sum(p<<1|1),void();}
    	inline void update(int p,int l,int r,int v)
    	{
    		if(l<=l(p)&&r(p)<=r) return sum(p)+=v*(r(p)-l(p)+1),add(p)+=v,void();
    		int mid=(l(p)+r(p))>>1; spread(p);
    		if(l<=mid) update(p<<1,l,r,v);
    		if(r>mid) update(p<<1|1,l,r,v); 
    		return push_up(p);
    	}
    	inline int ask(int p,int l,int r)
    	{
    		if(l<=l(p)&&r(p)<=r) return sum(p);
    		spread(p); int ans=0,mid=(l(p)+r(p))>>1;
    		if(l<=mid) ans+=ask(p<<1,l,r); if(r>mid) ans+=ask(p<<1|1,l,r);
    		return ans;
    	}
    	int ans[N];
    	struct node{
    		int l,r,id,val;
    	}; vector<node> vec1[N],vec2[N];
    	signed main()
    	{
    		n=read(); m=read(); p1=read(); p2=read();
    		for(int i=1;i<=n;++i) a[i]=read(); a[0]=a[n+1]=n+1;
    		for(int i=1;i<=n+1;++i) 
    		{
    			while(top&&a[st[top]]<a[i]) r[st[top]]=i,top--;
    			l[i]=st[top]; st[++top]=i;
    		}
    		for(int i=1;i<=m;++i) 
    		{
    			int tl=read(),tr=read();
    			ans[i]+=(tr-tl)*p1;
    			vec2[tl-1].push_back((node){tl,tr,i,0});
    			vec2[tr].push_back((node){tl,tr,i,0});
    		}
    		for(int i=1;i<=n;++i)
    		{
    			if(l[i]&&r[i]<=n) vec1[r[i]].push_back((node){l[i],l[i],0,p1});
    			if(l[i]&&r[i]>i+1) vec1[l[i]].push_back((node){i+1,r[i]-1,0,p2});
    			if(l[i]+1<i&&r[i]<=n) vec1[r[i]].push_back((node){l[i]+1,i-1,0,p2});
    		}
    		
    		build(1,1,n);
    		for(int i=1;i<=n;++i)
    		{
    			int siz=vec1[i].size();
    			for(int j=0;j<siz;++j) 
    			{
    				update(1,vec1[i][j].l,vec1[i][j].r,vec1[i][j].val);
    			}
    			siz=vec2[i].size();
    			for(int j=0;j<siz;++j) 
    			{
    				int sum=ask(1,vec2[i][j].l,vec2[i][j].r);
    				if(i==vec2[i][j].r) ans[vec2[i][j].id]+=sum; 
    				else ans[vec2[i][j].id]-=sum; 
    			}
    		}
    		for(int i=1;i<=m;++i) printf("%lld
    ",ans[i]);
    		return 0;
    	}
    }
    signed main(){return yspm::main();}
    

    Review

    (1.) 贡献法好!

    (2.) 提升代码能力和学习如何统计答案……

  • 相关阅读:
    CentOS+Phpstudy安装Sqli-Labs
    机器学习中的偏差与方差
    DVWA学习之SQL注入
    【转】EDNS
    【转】个人总结-网络安全学习和CTF必不可少的一些网站
    信息安全书单
    全球信息安全会议 Top 50
    【转】Linux编程之UDP SOCKET全攻略
    网络协议栈学习之重要的数据结构
    网络协议栈学习(二)创建 socket
  • 原文地址:https://www.cnblogs.com/yspm/p/12724839.html
Copyright © 2011-2022 走看看