• [2018 集训队互测 Day 1] 完美的队列


    一、题目

    点此看题

    二、解法

    突破本题的关键是全局询问,对于每个询问 \((l,r,x)\),考虑计算出它完全消失的时间 \(ed_i\),那么在 \([i,ed_i)\) 这段时间内权值 \(x\) 都是出现的。所以如果我们处理出了所有 \(ed_i\),可以直接回答询问。

    处理 \(ed_i\) 并不好直接 polylog,考虑将原序列分块。那么每个询问会被拆分成若干个整块和散块,我们分别处理整块和散块对询问的贡献即可。(贡献的含义是,把被弹出的时间拿给询问取最大值)

    考虑整块对询问的贡献,直接对所有询问 two-pointers,我们维护当前局面最大的 \(a_i\) 记为 \(mx\),再维护一个整体覆盖次数记为 \(tag\),那么如果 \(mx>tag\) 就右移右端点。

    加入一个询问时,如果它覆盖了整个块,那么直接修改 \(tag\),否则暴力修改这个块的一部分,暴力重新计算 \(mx\),移动左端点时删除询问。由于所有快重构的总次数不超过 \(O(m)\),均摊下来复杂度 \(O(m\sqrt n)\)

    考虑散块对询问的贡献,我们把在这个块内拥有散块的询问取出来,称这种询问为 \(z\) 询问。先枚举块内的单点,然后直接对 \(z\) 询问 two-pointers(必须要把这个单点弹出),需要预处理这些东西:

    • \(i\) 个询问覆盖整个块的总次数 \(pre_i\)
    • \(i\)\(z\) 询问的上一个覆盖整个块的询问编号 \(lst_i\)

    因为右端点不一定是 \(z\) 询问,可能是一个覆盖整个块的询问,这可以通过分类讨论解决。

    时间复杂度 \(O(m\sqrt n)\)

    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <cmath>
    using namespace std;
    const int M = 100005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,ans,a[M],b[M],l[M],r[M],x[M],ed[M];
    int mx,tg,L,R,c[M],d[M],pre[M],lst[M],cnt[M];
    vector<int> v[M];
    void get()
    {
    	mx=-1e9;
    	for(int i=L;i<=R;i++) mx=max(mx,b[i]);
    }
    void add(int l,int r,int c)
    {
    	if(l<=L && R<=r) {tg+=c;return ;}
    	if(L>r || l>R) return ;
    	for(int i=max(l,L);i<=min(r,R);i++)
    		b[i]+=c;
    	get();
    }
    void work()
    {
    	int t=0,o=0;get();tg=0;
    	for(int i=1,j=2;i<=m;i++)
    	{
    		while(j<=m && mx+tg>0)
    			add(l[j],r[j],-1),j++;
    		pre[i]=pre[i-1];
    		if(l[i]<=L && R<=r[i])
    		{
    			ed[i]=max(ed[i],mx+tg>0?m+1:j-1);
    			c[++t]=i;pre[i]++;
    		}
    		else if(L<=r[i] && l[i]<=R)
    			d[++o]=i,lst[o]=t;
    		add(l[i+1],r[i+1],1);
    	}
    	for(int i=L;i<=R;i++)
    	{
    		tg=a[i];
    		for(int j=1,k=1;j<=o;j++)
    		{
    			while(k<o && tg>0)
    				k++,tg-=pre[d[k]]-pre[d[k-1]]+
    				(l[d[k]]<=i && i<=r[d[k]]);
    			if(l[d[j]]<=i && i<=r[d[j]])
    			{
    				if(tg<=0)
    				{
    					int f=l[d[k]]<=i && i<=r[d[k]];
    					if(f && tg==0)
    						ed[d[j]]=max(ed[d[j]],d[k]);
    					else
    						ed[d[j]]=max(ed[d[j]],c[lst[k]+tg+f]);
    				}
    				else
    				{
    					if(tg<=pre[m]-pre[d[o]])
    						ed[d[j]]=max(ed[d[j]],c[lst[o]+tg]);
    					else
    						ed[d[j]]=m+1;
    				}
    			}
    			tg+=pre[d[j+1]]-pre[d[j]]+
    			(l[d[j+1]]<=i && i<=r[d[j+1]]);
    		}
    	}
    }
    signed main()
    {
    	n=read();m=read();k=sqrt(n);
    	for(int i=1;i<=n;i++) a[i]=b[i]=read();
    	for(int i=1;i<=m;i++)
    		l[i]=read(),r[i]=read(),x[i]=read();
    	for(int T=0;T*k<n;T++)
    		L=T*k+1,R=min(n,L+k-1),work();
    	for(int i=1;i<=m;i++)
    	{
    		if(!(cnt[x[i]]++)) ans++;
    		v[ed[i]].push_back(x[i]);
    		for(int x:v[i])
    			if(!(--cnt[x])) ans--;
    		printf("%d\n",ans);
    	}
    }
    
  • 相关阅读:
    effective c++ 条款10 让operator= 返回*this的引用
    基于vs2008的opengl开发环境的配置
    effective c++ 条款13 以对象管理资源
    effective C++ 条款06如果你不想让编译器为你生成函数就应该明确拒绝
    effective c++条款07为多态基类声明为virtual析构函数
    effective c++条款08别让异常逃离析构函数
    effective c++条款11 在operator=中处理“自我赋值”
    ubuntu 下的截图工具
    effective c++ 条款12 赋值对象时勿忘其每一个成员
    [linux 安装]修改centos的yum源
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16467765.html
Copyright © 2020-2023  润新知