zoukankan      html  css  js  c++  java
  • GYM103069G. Prof. Pang's sequence

    给出一个长度为\(n\)的序列\(a\)

    每次询问一个区间\([l,r]\)

    询问有多少个子区间\([i,j]\)满足子区间内不同的数的数量是奇数。

    做法:

    将询问离线,从左往右扫描右端点\(r\),维护\([1,r]\)这段序列的合法后缀的数量、不合法后缀的数量。

    当前右端点的值是\(a[r]\),记\(a[r]\)的上次出现位置\(pre\),那么\(r\)从上一格移到这一格,\(pre+1\)\(r\)这段区间的后缀,全都要取反,即合法变为不合法,不合法变为合法。

    然后对于当前右端点,在线段树上查询区间和\([l,r]\),就可以得到有多少个右端点是\(r\)的合法区间。

    这里有一个非常重要的技巧,对线段树的每个区间维护历史版本和,因为历史上的每个版本都对应一个\(r\)

    同时查询区间历史版本和\([l,r]\),就可以得到有多少个子区间\([i,j]\)满足\(l\leq i\leq j\leq r\),且子区间内的不同数的数量是奇数。

    接下来就需要线段树支持区间取反、区间求历史版本和。

    这里我只能借鉴了LGM的代码,自己实在是写不出来啊

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=5e5+10;
    struct node {
    	int l,r;
    	int rev;
    	int tag0,tag1;
    	int cnt;
    	ll sum;
    }segTree[maxn<<2];
    
    void add_rev (int u) {
    	segTree[u].cnt=segTree[u].r-segTree[u].l+1-segTree[u].cnt;
    	swap(segTree[u].tag0,segTree[u].tag1);
    	segTree[u].rev^=1;
    }
    void add_tag0 (int u,int x) {
    	segTree[u].sum+=1ll*x*(segTree[u].r-segTree[u].l+1-segTree[u].cnt);
    	segTree[u].tag0+=x;
    }
    void add_tag1 (int u,int x) {
    	segTree[u].sum+=1ll*x*segTree[u].cnt;
    	segTree[u].tag1+=x;
    }
    void pushdown (int u) {
    	if (segTree[u].rev) {
    		add_rev(u<<1);
    		add_rev(u<<1|1);
    		segTree[u].rev=0;
    	}
    	if (segTree[u].tag0) {
    		add_tag0(u<<1,segTree[u].tag0);
    		add_tag0(u<<1|1,segTree[u].tag0);
    		segTree[u].tag0=0;
    	}
    	if (segTree[u].tag1) {
    		add_tag1(u<<1,segTree[u].tag1);
    		add_tag1(u<<1|1,segTree[u].tag1);
    		segTree[u].tag1=0;
    	}
    }
    void pushup (int u) {
    	segTree[u].sum=segTree[u<<1].sum+segTree[u<<1|1].sum;
    	segTree[u].cnt=segTree[u<<1].cnt+segTree[u<<1|1].cnt;
    }
    void build (int u,int l,int r) {
    	segTree[u].l=l;
    	segTree[u].r=r;
    	if (l==r) return;
    	int mid=(l+r)>>1;
    	build(u<<1,l,mid);
    	build(u<<1|1,mid+1,r);
    }
    void update (int u,int l,int r) {
    	if (segTree[u].l>=l&&segTree[u].r<=r) {
    		add_rev(u);
    		return;
    	}
    	pushdown(u);
    	int mid=(segTree[u].l+segTree[u].r)>>1;
    	if (l<=mid) update(u<<1,l,r);
    	if (r>mid) update(u<<1|1,l,r);
    	pushup(u);
    }
    ll query (int u,int l,int r) {
    	if (segTree[u].l>=l&&segTree[u].r<=r) {
    		return segTree[u].sum;
    	}
    	pushdown(u);
    	int mid=(segTree[u].l+segTree[u].r)>>1;
    	ll ans=0;
    	if (l<=mid) ans+=query(u<<1,l,r);
    	if (r>mid) ans+=query(u<<1|1,l,r);
    	return ans;
    } 
    vector<pair<int,int> > g[maxn];
    ll ans[maxn];
    int n,m,a[maxn],pre[maxn];
    
    int main () {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++) scanf("%d",a+i);
    	scanf("%d",&m);
    	for (int i=1;i<=m;i++) {
    		int l,r;
    		scanf("%d%d",&l,&r);
    		g[r].push_back({l,i});
    	}
    	build(1,1,n);
    	for (int i=1;i<=n;i++) {
    		update(1,pre[a[i]]+1,i);
    		add_tag1(1,1);
    		for (auto it: g[i]) {
    			int x=it.first;
    			int y=it.second;
    			ans[y]=query(1,x,i);
    		}
    		pre[a[i]]=i;
    	}
    	for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    }
  • 相关阅读:
    魔兽争霸RPG地图开发教程2
    魔兽争霸RPG地图开发教程1
    php mysql decimal 多余的0 解决方案
    windows下创建子进程过程中代码重复执行问题
    python多进程的理解 multiprocessing Process join run
    进程和线程的概念、区别和联系
    Python中的魔术方法详解(双下方法)
    socketserver源码剖析
    Socketserver详解
    全网最详细python中socket套接字send与sendall的区别
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15534126.html
Copyright © 2011-2022 走看看