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. 【UE4 C++ 基础知识】<6> 容器——TMap

    概述 TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中. 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取 映 ...

  2. [Git系列] 前言

    Git 简介 Git 是一个重视速度的分布式版本控制和代码管理系统,最初是由 Linus Torvalds 为开发 Linux 内核而设计并开发的,是一款遵循二代 GUN 协议的免费软件.这一教程会向 ...

  3. Go语言核心36讲(Go语言进阶技术七)--学习笔记

    13 | 结构体及其方法的使用法门 我们都知道,结构体类型表示的是实实在在的数据结构.一个结构体类型可以包含若干个字段,每个字段通常都需要有确切的名字和类型. 前导内容:结构体类型基础知识 当然了,结 ...

  4. 第2次 Beta Scrum Meeting

    本次会议为Beta阶段第2次Scrum Meeting会议 会议概要 会议时间:2021年5月31日 会议地点:「腾讯会议」线上进行 会议时长:0.5小时 会议内容简介:对完成工作进行阶段性汇报:对下 ...

  5. Spring Authorization Server的使用

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

  6. 牛客网 剑指Offer 索引

    二维数组中的查找 替换空格 从尾到头打印链表 重建二叉树 用两个栈实现队列 旋转数组的最小数字 斐波那契数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整数次方 调整数组顺序使奇数位于偶数 ...

  7. Vue 基础自查——watch、computed和methods的区别

    1 前言 创建一个Vue实例时,可以传入一个选项对象 const vm = new Vue({ data: { msg: 'hello' }, computed: {}, methods: {}, w ...

  8. (转)刚来的大神彻底干掉了代码中的if else...

    一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性. 首先可读性,不言而喻,过多的 if-else 代码和嵌套,会使阅读代码的人很难理解到底是什么意思.尤其是那些没有注释的代码. 其次 ...

  9. TTMS 一个基于Java Swing的Socket通信的剧院票务管理系统

    TTMS (Theater Ticket Management System) 点我进入github TTMS全称剧院票务管理系统,分为客户端和服务器端.服务器端可以接收客户端连接请求,客户端相当于我 ...

  10. k8s入坑之路(5)kube-apiserver详解

    API Server kube-apiserver 是 Kubernetes 最重要的核心组件之一,主要提供以下的功能 提供集群管理的 REST API 接口,包括认证授权.数据校验以及集群状态变更等 ...