Codeforces 788E - New task(线段树)
这是一道 *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(线段树)的更多相关文章
- Buses and People CodeForces 160E 三维偏序+线段树
Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)
[Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...
- [Codeforces 1199D]Welfare State(线段树)
[Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...
- [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)
[Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...
- 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 ...
- 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 ...
- Codeforces 588E. A Simple Task (线段树+计数排序思想)
题目链接:http://codeforces.com/contest/558/problem/E 题意:有一串字符串,有两个操作:1操作是将l到r的字符串升序排序,0操作是降序排序. 题解:建立26棵 ...
- 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 ...
随机推荐
- 【UE4 C++ 基础知识】<6> 容器——TMap
概述 TMap主要由两个类型定义(一个键类型和一个值类型),以关联对的形式存储在映射中. 将数据存储为键值对(TPair<KeyType, ValueType>),只将键用于存储和获取 映 ...
- [Git系列] 前言
Git 简介 Git 是一个重视速度的分布式版本控制和代码管理系统,最初是由 Linus Torvalds 为开发 Linux 内核而设计并开发的,是一款遵循二代 GUN 协议的免费软件.这一教程会向 ...
- Go语言核心36讲(Go语言进阶技术七)--学习笔记
13 | 结构体及其方法的使用法门 我们都知道,结构体类型表示的是实实在在的数据结构.一个结构体类型可以包含若干个字段,每个字段通常都需要有确切的名字和类型. 前导内容:结构体类型基础知识 当然了,结 ...
- 第2次 Beta Scrum Meeting
本次会议为Beta阶段第2次Scrum Meeting会议 会议概要 会议时间:2021年5月31日 会议地点:「腾讯会议」线上进行 会议时长:0.5小时 会议内容简介:对完成工作进行阶段性汇报:对下 ...
- Spring Authorization Server的使用
Spring Authorization Server的使用 一.背景 二.前置知识 三.需求 四.核心代码编写 1.引入授权服务器依赖 2.创建授权服务器用户 3.创建授权服务器和客户端 五.测试 ...
- 牛客网 剑指Offer 索引
二维数组中的查找 替换空格 从尾到头打印链表 重建二叉树 用两个栈实现队列 旋转数组的最小数字 斐波那契数列 跳台阶 变态跳台阶 矩形覆盖 二进制中1的个数 数值的整数次方 调整数组顺序使奇数位于偶数 ...
- Vue 基础自查——watch、computed和methods的区别
1 前言 创建一个Vue实例时,可以传入一个选项对象 const vm = new Vue({ data: { msg: 'hello' }, computed: {}, methods: {}, w ...
- (转)刚来的大神彻底干掉了代码中的if else...
一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性. 首先可读性,不言而喻,过多的 if-else 代码和嵌套,会使阅读代码的人很难理解到底是什么意思.尤其是那些没有注释的代码. 其次 ...
- TTMS 一个基于Java Swing的Socket通信的剧院票务管理系统
TTMS (Theater Ticket Management System) 点我进入github TTMS全称剧院票务管理系统,分为客户端和服务器端.服务器端可以接收客户端连接请求,客户端相当于我 ...
- k8s入坑之路(5)kube-apiserver详解
API Server kube-apiserver 是 Kubernetes 最重要的核心组件之一,主要提供以下的功能 提供集群管理的 REST API 接口,包括认证授权.数据校验以及集群状态变更等 ...