Codeforces 788E - New task(线段树)

Codeforces 题目传送门 & 洛谷题目传送门

这是一道 *2900 的 D1E,而且被!我!自!己!搞!出!来!了!

虽然我承认它难度及摆放的位置异常异常虚高,并且就算我到了现场也不可能切掉

下记 \(t_x\) 表示 \(x\) 是否可以被选择。

首先题目要我们统计五元组 \((a,b,c,d,e)\) 的个数,但稍微观察下就能发现 \(a,e\) 的个数是很好维护的,并且它不受到 \(t_x\) 的值的影响,因此我们可以提前把它预处理出来,记 \(pre_i=\sum\limits_{j=1}^{i-1}[a_j\le a_i]\)\(nxt_i=\sum\limits_{j=i+1}^n[a_j\le a_i]\),那么显然对于固定的三元组 \((b,c,d)\) 合法的 \(a\) 的个数为 \(pre_b\),合法的 \(e\) 的个数为 \(nxt_d\),也就是说要求的这东西等价于 \(\sum\limits_{1\le b\le c\le d\le n}pre_b·nxt_d·[a_b=a_c=a_d]\)

其次考虑一次修改对答案的影响,显然每次将某个点 \(x\) 由可选状态设为不可选状态,答案的变化量为包含 \(x\) 的三元组 \((b,c,d)\) 的贡献的相反数;反之也同理。因此我们只需维护包含 \(x\) 的三元组 \((b,c,d)\) 的贡献之和。这个可以分三种情况:

  • \(c=x\),由于 \(b\)\(d\) 的贡献相互独立,故可算出 \(b,d\) 的贡献后用乘法原理将其乘起来,即 \(\sum\limits_{b=1}^{x-1}pre_b[a_b=a_x\land t_b=1]\times\sum\limits_{d=x+1}^nnxt_d[a_d=a_x\land t_d=1]\)

  • \(b=x\),我们记 \(s_i=\sum\limits_{j=1}^i[a_j=a_i\land t_j=1]\),考虑枚举 \(d\),那么合法的 \(c\) 的个数即为 \(s_d-s_x-1\),即 \(\sum\limits_{d=x+1}^n(s_d-s_x-1)\times nxt_d\times[a_d=a_x\land t_d=1]\)

  • \(d=x\),与 \(b=x\) 的情况类似,这里就不再赘述了。

考虑怎样维护这个东西。我们将 \(a_i\) 离散化并对每个 \(a_i\) 建一棵动态开点线段树。具体来说假设对于 \(x\) 这个值存在 \(k_x\)\(a_t=x\)\(t\),从小到大依次是 \(p_1,p_2,\dots,p_{k_x}\),那么我们就建一棵长度为 \(k_x\) 的线段树,线段树上每一个区间 \([l,r]\) 维护以下五个值:

  • \(pre0:\sum\limits_{i=l}^rpre_{p_i}\times t_{p_i}\)
  • \(nxt0:\sum\limits_{i=l}^rnxt_{p_i}\times t_{p_i}\)
  • \(pre1:\sum\limits_{i=l}^rpre_{p_i}\times t_{p_i}\times s_{p_i}\)
  • \(nxt1:\sum\limits_{i=l}^rnxt_{p_i}\times t_{p_i}\times s_{p_i}\)
  • \(cnt:\sum\limits_{i=l}^rt_{p_i}\)

那么 \(c=x\) 的情况我们只需查一遍 \(pre0,nxt0\) 即可,\(b=x\) 的情况我们可将询问改写成 \(\sum\limits_{d=x+1}^ns_d\times nxt_d\times t_d\times[a_d=a_x]-(s_x+1)\times\sum\limits_{d=x+1}^ns_d\times t_d\times[a_d=a_x]\),这样我们只需查一遍 \(cnt,nxt0,nxt1\) 即可。\(d=x\) 的情况也可以采用类似的改写方式,只需查一遍 \(cnt,pre0,pre1\) 即可,这样我们即可在对数时间内求出包含 \(x\) 的三元组 \((b,c,d)\) 的贡献。

最后考虑修改对线段树维护的五个值的影响,显然最初所有的 \(s_{p_i}=i,t_{p_i}=1\),所以我们令所有叶子节点 \([i,i]\)\(pre0\)\(pre_{p_i}\)\(nxt0\)\(nxt_{p_i}\)\(pre1\)\(pre_{p_i}\times i\)\(nxt1\)\(nxt_{p_i}\times i\)\(cnt\)\(1\),而对于一次修改操作,假设我们修改了 \(x\) 的状态,那么显然我们需要进行两次操作:

  • \(t_x=0\)\(t_x=1\)
  • 假设 \(x\)\(a_x\) 的线段树上的位置为 \(p\),那么我们需令 \([p+1,k_{a_x}]\)\(s\) 值加 \(1\) 或减 \(1\)

第一个操作显然就递归到叶子节点单调修改即可。第二个操作就在对应区间上执行区间加操作,假设我们要对 \([l,r]\) 区间的 \(s\) 值执行 \(+x\) 操作,那么我们显然就令 \(pre_1\leftarrow pre_1+pre_0\times x\)\(nxt_1\leftarrow nxt_1+nxt_0\times x\),直接打个懒标记就行了。

时间复杂度 \(n\log n\),常数有亿点大,因此又一次荣膺了最劣解

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int MOD=1e9+7;
const int INV3=333333336;
int n,qu,a[MAXN+5],key[MAXN+5],uni[MAXN+5],num=0;
int t[MAXN+5],pre[MAXN+5],nxt[MAXN+5];
void add(int x,int v){for(int i=x;i<=num;i+=(i&-i)) t[i]+=v;}
int ask(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;}
vector<int> pos[MAXN+5];int where[MAXN+5],cnt[MAXN+5];
struct node{
	int ch[2];
	int lz_pre,lz_nxt,cnt;
	int pre0,pre1;
	int nxt0,nxt1;
} s[MAXN*4+5];
int rt[MAXN+5],ncnt=0;
void pushup(int k){
	s[k].pre0=(s[s[k].ch[0]].pre0+s[s[k].ch[1]].pre0)%MOD;
	s[k].pre1=(s[s[k].ch[0]].pre1+s[s[k].ch[1]].pre1)%MOD;
	s[k].nxt0=(s[s[k].ch[0]].nxt0+s[s[k].ch[1]].nxt0)%MOD;
	s[k].nxt1=(s[s[k].ch[0]].nxt1+s[s[k].ch[1]].nxt1)%MOD;
	s[k].cnt=s[s[k].ch[0]].cnt+s[s[k].ch[1]].cnt;
}
void build(int &k,int l,int r,int v){
	k=++ncnt;
	if(l==r){
		s[k].cnt=1;
		s[k].pre0=pre[pos[v][l-1]];s[k].pre1=1ll*l*s[k].pre0%MOD;
		s[k].nxt0=nxt[pos[v][l-1]];s[k].nxt1=1ll*(cnt[v]-l+1)*s[k].nxt0%MOD;
		return;
	} int mid=l+r>>1;
	build(s[k].ch[0],l,mid,v);build(s[k].ch[1],mid+1,r,v);
	pushup(k);
}
void pushdown(int k){
	if(s[k].lz_pre){
		s[s[k].ch[0]].pre1=(s[s[k].ch[0]].pre1+1ll*s[k].lz_pre*s[s[k].ch[0]].pre0%MOD)%MOD;
		s[s[k].ch[0]].lz_pre=(s[s[k].ch[0]].lz_pre+s[k].lz_pre)%MOD;
		s[s[k].ch[1]].pre1=(s[s[k].ch[1]].pre1+1ll*s[k].lz_pre*s[s[k].ch[1]].pre0%MOD)%MOD;
		s[s[k].ch[1]].lz_pre=(s[s[k].ch[1]].lz_pre+s[k].lz_pre)%MOD;
		s[k].lz_pre=0;
	} if(s[k].lz_nxt){
		s[s[k].ch[0]].nxt1=(s[s[k].ch[0]].nxt1+1ll*s[k].lz_nxt*s[s[k].ch[0]].nxt0%MOD)%MOD;
		s[s[k].ch[0]].lz_nxt=(s[s[k].ch[0]].lz_nxt+s[k].lz_nxt)%MOD;
		s[s[k].ch[1]].nxt1=(s[s[k].ch[1]].nxt1+1ll*s[k].lz_nxt*s[s[k].ch[1]].nxt0%MOD)%MOD;
		s[s[k].ch[1]].lz_nxt=(s[s[k].ch[1]].lz_nxt+s[k].lz_nxt)%MOD;
		s[k].lz_nxt=0;
	}
}
int query_cnt(int k,int l,int r,int ql,int qr){
	if(ql>qr) return 0;
	if(ql<=l&&r<=qr) return s[k].cnt;
	pushdown(k);int mid=l+r>>1;
	if(qr<=mid) return query_cnt(s[k].ch[0],l,mid,ql,qr);
	else if(ql>mid) return query_cnt(s[k].ch[1],mid+1,r,ql,qr);
	else return query_cnt(s[k].ch[0],l,mid,ql,mid)+query_cnt(s[k].ch[1],mid+1,r,mid+1,qr);
}
int query_pre0(int k,int l,int r,int ql,int qr){
	if(ql>qr) return 0;
	if(ql<=l&&r<=qr) return s[k].pre0;
	pushdown(k);int mid=l+r>>1;
	if(qr<=mid) return query_pre0(s[k].ch[0],l,mid,ql,qr);
	else if(ql>mid) return query_pre0(s[k].ch[1],mid+1,r,ql,qr);
	else return (query_pre0(s[k].ch[0],l,mid,ql,mid)+query_pre0(s[k].ch[1],mid+1,r,mid+1,qr))%MOD;
}
int query_pre1(int k,int l,int r,int ql,int qr){
	if(ql>qr) return 0;
	if(ql<=l&&r<=qr) return s[k].pre1;
	pushdown(k);int mid=l+r>>1;
	if(qr<=mid) return query_pre1(s[k].ch[0],l,mid,ql,qr);
	else if(ql>mid) return query_pre1(s[k].ch[1],mid+1,r,ql,qr);
	else return (query_pre1(s[k].ch[0],l,mid,ql,mid)+query_pre1(s[k].ch[1],mid+1,r,mid+1,qr))%MOD;
}
int query_nxt0(int k,int l,int r,int ql,int qr){
	if(ql>qr) return 0;
	if(ql<=l&&r<=qr) return s[k].nxt0;
	pushdown(k);int mid=l+r>>1;
	if(qr<=mid) return query_nxt0(s[k].ch[0],l,mid,ql,qr);
	else if(ql>mid) return query_nxt0(s[k].ch[1],mid+1,r,ql,qr);
	else return (query_nxt0(s[k].ch[0],l,mid,ql,mid)+query_nxt0(s[k].ch[1],mid+1,r,mid+1,qr))%MOD;
}
int query_nxt1(int k,int l,int r,int ql,int qr){
	if(ql>qr) return 0;
	if(ql<=l&&r<=qr) return s[k].nxt1;
	pushdown(k);int mid=l+r>>1;
	if(qr<=mid) return query_nxt1(s[k].ch[0],l,mid,ql,qr);
	else if(ql>mid) return query_nxt1(s[k].ch[1],mid+1,r,ql,qr);
	else return (query_nxt1(s[k].ch[0],l,mid,ql,mid)+query_nxt1(s[k].ch[1],mid+1,r,mid+1,qr))%MOD;
}
void modify(int k,int l,int r,int p,int x,int ps,int ss,int v){
	if(l==r){
		if(v==-1){
			s[k].cnt=s[k].pre0=s[k].pre1=0;
			s[k].nxt0=s[k].nxt1=0;
		} else {
			s[k].cnt=1;
			s[k].pre0=pre[x];s[k].pre1=1ll*pre[x]*ps%MOD;
			s[k].nxt0=nxt[x];s[k].nxt1=1ll*nxt[x]*ss%MOD;
		} return;
	} pushdown(k);int mid=l+r>>1;
	if(p<=mid) modify(s[k].ch[0],l,mid,p,x,ps,ss,v);
	else modify(s[k].ch[1],mid+1,r,p,x,ps,ss,v);
	pushup(k);
}
void modify_pre(int k,int l,int r,int ql,int qr,int x){
	if(ql>qr) return;
	if(ql<=l&&r<=qr){
		s[k].pre1=(s[k].pre1+1ll*x*s[k].pre0%MOD)%MOD;
		s[k].lz_pre=(s[k].lz_pre+x)%MOD;return;
	} pushdown(k);int mid=l+r>>1;
	if(qr<=mid) modify_pre(s[k].ch[0],l,mid,ql,qr,x);
	else if(ql>mid) modify_pre(s[k].ch[1],mid+1,r,ql,qr,x);
	else modify_pre(s[k].ch[0],l,mid,ql,mid,x),modify_pre(s[k].ch[1],mid+1,r,mid+1,qr,x);
	pushup(k);
}
void modify_nxt(int k,int l,int r,int ql,int qr,int x){
	if(ql>qr) return;
	if(ql<=l&&r<=qr){
		s[k].nxt1=(s[k].nxt1+1ll*x*s[k].nxt0%MOD)%MOD;
		s[k].lz_nxt=(s[k].lz_nxt+x)%MOD;return;
	} pushdown(k);int mid=l+r>>1;
	if(qr<=mid) modify_nxt(s[k].ch[0],l,mid,ql,qr,x);
	else if(ql>mid) modify_nxt(s[k].ch[1],mid+1,r,ql,qr,x);
	else modify_nxt(s[k].ch[0],l,mid,ql,mid,x),modify_nxt(s[k].ch[1],mid+1,r,mid+1,qr,x);
	pushup(k);
}
int get(int x){
	if(!query_cnt(rt[a[x]],1,cnt[a[x]],where[x],where[x])) return 0;
	int ret=0;
	ret=(ret+1ll*query_pre0(rt[a[x]],1,cnt[a[x]],1,where[x]-1)*
				 query_nxt0(rt[a[x]],1,cnt[a[x]],where[x]+1,cnt[a[x]]))%MOD;
	ret=(ret+1ll*nxt[x]*
		(1ll*(query_cnt(rt[a[x]],1,cnt[a[x]],1,where[x])-1)*
			  query_pre0(rt[a[x]],1,cnt[a[x]],1,where[x]-1)%MOD-
			  query_pre1(rt[a[x]],1,cnt[a[x]],1,where[x]-1)+MOD))%MOD;
	ret=(ret+1ll*pre[x]*
		(1ll*(query_cnt(rt[a[x]],1,cnt[a[x]],where[x],cnt[a[x]])-1)*
			  query_nxt0(rt[a[x]],1,cnt[a[x]],where[x]+1,cnt[a[x]])%MOD-
			  query_nxt1(rt[a[x]],1,cnt[a[x]],where[x]+1,cnt[a[x]])+MOD))%MOD;
	return ret;
}
void toggle(int x,int v){
	if(v==-1){
		modify(rt[a[x]],1,cnt[a[x]],where[x],x,0,0,-1);
		modify_pre(rt[a[x]],1,cnt[a[x]],where[x]+1,cnt[a[x]],MOD-1);
		modify_nxt(rt[a[x]],1,cnt[a[x]],1,where[x]-1,MOD-1);
	} else {
		int ps=query_cnt(rt[a[x]],1,cnt[a[x]],1,where[x]-1);
		int ss=query_cnt(rt[a[x]],1,cnt[a[x]],where[x]+1,cnt[a[x]]);
		modify(rt[a[x]],1,cnt[a[x]],where[x],x,ps+1,ss+1,1);
		modify_pre(rt[a[x]],1,cnt[a[x]],where[x]+1,cnt[a[x]],1);
		modify_nxt(rt[a[x]],1,cnt[a[x]],1,where[x]-1,1);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),key[i]=a[i];
	sort(key+1,key+n+1);
	for(int i=1;i<=n;i++) if(key[i]^key[i-1]) uni[++num]=key[i];
	for(int i=1;i<=n;i++) a[i]=lower_bound(uni+1,uni+num+1,a[i])-uni;
	for(int i=1;i<=n;i++) add(a[i],1),pre[i]=ask(a[i])-1;memset(t,0,sizeof(t));
	for(int i=n;i;i--) add(a[i],1),nxt[i]=ask(a[i])-1;
	for(int i=1;i<=n;i++) pos[a[i]].push_back(i),where[i]=pos[a[i]].size();
	for(int i=1;i<=n;i++) cnt[a[i]]++;
	for(int i=1;i<=num;i++) build(rt[i],1,cnt[i],i);
	int ans=0;
	for(int i=1;i<=n;i++) ans=(ans+get(i))%MOD;
	ans=1ll*ans*INV3%MOD;
	int qu;scanf("%d",&qu);
	while(qu--){
		int opt,x;scanf("%d%d",&opt,&x);
		if(opt==1){
			ans=(ans-get(x)+MOD)%MOD;
			toggle(x,-1);
		} else {
			toggle(x,1);
			ans=(ans+get(x))%MOD;
		}
		printf("%d\n",ans);
	}
	return 0;
}
/*
9
3 4 4 2 4 5 4 4 1
3
1 5
2 5
1 2
*/
posted @ 2021-03-16 16:37  tzc_wk  阅读(84)  评论(0)    收藏  举报