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
*/

Codeforces 788E - New task(线段树)的更多相关文章

  1. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  2. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  3. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  4. [Codeforces 1199D]Welfare State(线段树)

    [Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...

  5. [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)

    [Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...

  6. Codeforces Round #312 (Div. 2) E. A Simple Task 线段树

    E. A Simple Task 题目连接: http://www.codeforces.com/contest/558/problem/E Description This task is very ...

  7. Codeforces Round #312 (Div. 2) E. A Simple Task 线段树+计数排序

    题目链接: http://codeforces.com/problemset/problem/558/E E. A Simple Task time limit per test5 secondsme ...

  8. Codeforces 588E. A Simple Task (线段树+计数排序思想)

    题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...

  9. CodeForces 588E A Simple Task(线段树)

    This task is very simple. Given a string S of length n and q queries each query is on the format i j ...

随机推荐

  1. IEEE754浮点数的转换

    将十进制数转换为单精度浮点数 如何将十进制数转换为单精度浮点数参考 首先要知道 IEEE浮点标准:V=(-1)^s * M * 2^E 1.符号(sign)s决定这个数是负数(s=1)还是正数,0(s ...

  2. vue.$nextTick实现原理

    源码: const callbacks = [] let pending = false function flushCallbacks () { pending = false const copi ...

  3. 小白自制Linux开发板 八. Linux音频驱动配置

    不知不觉小白自制开发板系列已经到第八篇了,本篇要配置的是音频驱动,也算是硬件部分的最后一片了,积攒的文章也差不多全放完了,后续更新可能会放缓,还请见谅. 对于F1C200s是自带了多媒体处理功能的,所 ...

  4. ORA-19815: WARNING: db_recovery_file_dest_size闪回区爆满问题处理

    问题描述:有一个数据库起不来了,根据层层排查,是因为归档设置在了闪回区,文件的大小已经超出了闪回区限制.最后直接给数据库拖挂 环境:windows server2012 , oracle 19c,单机 ...

  5. 【数据结构与算法Python版学习笔记】树——树的遍历 Tree Traversals

    遍历方式 前序遍历 在前序遍历中,先访问根节点,然后递归地前序遍历左子树,最后递归地前序遍历右子树. 中序遍历 在中序遍历中,先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树. 后序遍 ...

  6. Spring Authorization Server的使用

    Spring Authorization Server的使用 一.背景 二.前置知识 三.需求 四.核心代码编写 1.引入授权服务器依赖 2.创建授权服务器用户 3.创建授权服务器和客户端 五.测试 ...

  7. 并发编程从零开始(九)-ConcurrentSkipListMap&Set

    并发编程从零开始(九)-ConcurrentSkipListMap&Set CAS知识点补充: 我们都知道在使用 CAS 也就是使用 compareAndSet(current,next)方法 ...

  8. 2021.9.25考试总结[NOIP模拟61]

    终于有点阳间题了然而挂了60pts 哈哈 T1 交通 类似简单题,限制看似很复杂,但不难发现当确定一条边是否被删后会产生裙带关系,很多边会跟着自动被确定是否被删. 仔细观察可以得出这种关系会构成偶环结 ...

  9. 计算机网络之应用层概述(C/S模型与p2p模型)

    文章转自:https://blog.csdn.net/weixin_43914604/article/details/105582318 学习课程:<2019王道考研计算机网络> 学习目的 ...

  10. EasyX安装教程

    Easyx是什么 就是一款可以在Windows里让你的C++程序里显示图片等的工具. 注意:EasyX不支持Linux.MacOS.不过还有Qt等可以选择. 安装VC/VS Easyx只支持Visua ...