题意:给出\(a[1...n]\),共\(n\)次操作,每次删除一个位置\(p_i\)(强制在线),此时区间会变为两个分离的区间,求每次操作的最大区间逆序对

首先要知道必要的工具,按权值建立的主席树可以在\(O(nlogn)\)内得到任意区间长为\(n\)的逆序对

但每次切除的点如果是不断沿着边来切,那么要统计的区间总长是\(O(n^2)\)

考虑逆序对的贡献是可以互为计算得到的,那么能否每次只对相对较小的区间的复杂度进行统计,得到\(O(nlogn)\)的区间统计总长?

如果已知一个区间\([L,R]\)的总逆序对\(oldans\),切点为\(M\),且更靠左边,

那可以对小区间的逆序数对进行暴力统计,得到\([L,M-1]\)的所有相互贡献,此时记为\(ans1\)

然后再\(O(logn)\)计算\(a[M]\)对\([L,M-1]\)的贡献,记为\(tmp\)

最后询问\(T[R]-T[K]\)的子树中比\(a[i],i∈[L,M]\)小的个数,记为\(tmp2\)

那么区间\([L,M-1]\)的逆序数对为\(ans1\),\([M+1,R]\)的逆序数对为\(oldans-ans1-tmp-tmp2\)

以上操作均可以在\(O(nlogn)\)内完成,\(n\)为小区间的长度

切点靠右同理,因此总的复杂度为\(O(nlog^2n)\)

还有一些细节需要处理,如何在一开始就得知\([L,R]\)的总逆序数对,

初始化时先计算出\([1,n]\)的贡献,剩下的必然在前面的操作中已经统计得到\(ans1\)或\(ans2\)

考虑区间总是不断分裂的,我们可以用简单的平衡树来维护位置\(M\)对应最近的\(L\)和\(R\)

并且答案总是对应于每一个\(L\),都有一个唯一的\(R\)和\(ans\)相对应,这部分只需用简单的数组即可得到

而\(ans\)的最优解需要在分裂时及时删除对应的元素,再多拿一个multiset动态维护最大值即可

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<set>
#include<ctime>
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i])
#define fastIO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define println(x) printf("%lld\n",(ll)(x))
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pr;
const int MAXN = 1e5+11;
const int MAXM = 4e6+11;
const int MOD = 142857;
const int INF = 1<<30;
int T[MAXN];
int a[MAXN],p[MAXN];
struct FST{
int cnt[MAXM];
int lc[MAXM],rc[MAXM];
int tot;
void init(){tot=0;lc[tot]=rc[tot]=cnt[tot]=0;}
int build(int l,int r){
int cur=++tot;
lc[cur]=rc[cur]=cnt[cur]=0;
if(l==r) return cur;
int mid=l+r>>1;
lc[cur]=build(l,mid);
rc[cur]=build(mid+1,r);
return cur;
}
int update(int old,int l,int r,int k,int v){
int cur=++tot;
lc[cur]=lc[old];
rc[cur]=rc[old];
cnt[cur]=cnt[old]+v;
if(l==r) return cur;
int mid=l+r>>1;
if(k<=mid) lc[cur]=update(lc[old],l,mid,k,v);
else rc[cur]=update(rc[old],mid+1,r,k,v);
return cur;
}
ll query(int cur1,int cur2,int l,int r,int L,int R){//l r
if((cur2|cur1)==0) return 0;
if(L<=l&&r<=R) return cnt[cur2]-cnt[cur1];
ll ans=0;
int mid=l+r>>1;
if(L<=mid) ans+=query(lc[cur1],lc[cur2],l,mid,L,R);
if(R>mid) ans+=query(rc[cur1],rc[cur2],mid+1,r,L,R);
return ans;
}
}fst;
multiset<int> st;
//multiset<pr> inv[MAXN];
ll inv[MAXN],invans[MAXN];
multiset<ll> ans;
int main(){
#ifndef ONLINE_JUDGE
freopen("stdin.txt","r",stdin);
#endif
int Test;
scanf("%d",&Test);
while(Test--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
if(n==1){
printf("0\n");
continue;
}
fst.init();
T[0]=fst.build(1,n);
for(int i=1;i<=n;i++){
T[i]=fst.update(T[i-1],1,n,a[i],1);
}
st.clear(); ans.clear();
st.insert(0),st.insert(n+1);
for(int i=0;i<=n+1;i++) inv[i]=0;
ll lastans=0;
for(int i=1;i<=n;i++){
if(a[i]!=1){
lastans+=fst.query(T[i],T[n],1,n,1,a[i]-1);
}
}
inv[1]=n,invans[1]=lastans;
ans.insert(lastans);
printf("%lld ",lastans);
ll mx=lastans;
for(int i=1;i<n;i++){
int k=lastans^p[i];
st.insert(k);
multiset<int>::iterator it,lo,hi;
lo=hi=it=st.find(k);
--lo,hi++;//被砍的位置
int l=*lo,r=*hi;
l++,--r;//实际操作的位置
ll oldans=invans[l];
ans.erase(ans.find(invans[l]));//这个答案必然无效
if(l==k&&r==k){
ans.insert(0),ans.insert(0);
printf("%lld%c",lastans,i==n-1?'\n':' ');
continue;
}
if(l==k){
ll tmp=0;
if(a[k]>1) tmp=fst.query(T[k],T[r],1,n,1,a[k]-1);
inv[k+1]=r;
invans[k+1]=oldans-tmp;
ans.insert(0);
ans.insert(oldans-tmp);
lastans=*--ans.end();
printf("%lld%c",lastans,i==n-1?'\n':' ');
continue;
}
if(r==k){
inv[l]=0;
ll tmp=0;
if(a[k]<n) tmp=fst.query(T[l-1],T[k-1],1,n,a[k]+1,n);
inv[l]=k-1;
invans[l]=oldans-tmp;
ans.insert(0);
ans.insert(oldans-tmp);
lastans=*--ans.end();
printf("%lld%c",lastans,i==n-1?'\n':' ');
continue;
}
int len1=k-l+1,len2=r-k+1;
ll ans1=0,ans2=0;
if(len1<len2){
for(int j=l;j<k;j++){
if(a[j]>1){
ans1+=fst.query(T[j],T[k-1],1,n,1,a[j]-1);
}
} ll tmp=0;
if(a[k]<n) tmp+=fst.query(T[l-1],T[k-1],1,n,a[k]+1,n);
// multiset<pr>::iterator t=inv[l].begin();
ans2=invans[l]; ans2-=(ans1+tmp);//此时包括a[k]的逆序对贡献
for(int j=l;j<=k;j++){
if(a[j]>1){
ans2-=fst.query(T[k],T[r],1,n,1,a[j]-1);//[k+1,r]
}
}
inv[l]=k-1; invans[l]=ans1;
inv[k+1]=r; invans[k+1]=ans2;
}else{
for(int j=k+1;j<=r;j++){
if(a[j]>1){
ans1+=fst.query(T[j],T[r],1,n,1,a[j]-1);//[k+1,r]
}
} ll tmp=0;
if(a[k]>1) tmp+=fst.query(T[k],T[r],1,n,1,a[k]-1);//[k+1,r]
// multiset<pr>::iterator t=inv[l].begin();
ans2=invans[l]; ans2-=(ans1+tmp);
for(int j=k;j<=r;j++){
if(a[j]<n){
ans2-=fst.query(T[l-1],T[k-1],1,n,a[j]+1,n);//[l,k-1]
}
}
inv[l]=k-1; invans[l]=ans2;
inv[k+1]=r; invans[k+1]=ans1;//.insert(pr(k-1,ans2));
//inv[k+1].insert(pr(r,ans1));
}
ans.insert(ans1);
ans.insert(ans2);
printf("%lld%c",*--ans.end(),i==n-1?'\n':' ');
lastans=*--ans.end();
}
}
return 0;
}

2018青岛网络赛G - Couleur 区间上的启发式合并的更多相关文章

  1. 2018北京网络赛 G The Mole /// 分块暴力 点线距离

    题目大意: 给定n段线段 编号为1~n 接下来m个询问 给定一点 输出离该点最近的线段的最小编号(距离相等时取编号小的) 题解 大致就是 1.坐标范围为(0,2^16-1) 将坐标系划分为2^8*2^ ...

  2. ACM-ICPC 2018青岛网络赛-H题 Traveling on the Axis

    题目:略(不知道怎么从ZOJ搬题) 地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4054 把这题的每个点分成两种情况 ...

  3. HDU 5884 Sort(2016年青岛网络赛 G 二分+贪心+小优化)

    好题 题意:给你n<=100000个数,每个数范围[0,1000],然后给你一个最大的代价T,每次最多合并k个数成为一个数,代价为k个数的总和.问最后合成1个数的总代价不大于T的最小k 题解:我 ...

  4. 2018 焦作网络赛 G Give Candies ( 欧拉降幂 )

    题目链接 题意 : 给出 N 个糖果.老师按顺序给 1~N 编号的学生分配糖果.每个学生要么不分.要么最少分一个.且由于是按顺序发放.那么对于某个有分到糖果的编号为 i 的学生.则 1~(i-1) 这 ...

  5. 2018 CCPC网络赛

    2018 CCPC网络赛 Buy and Resell 题目描述:有一种物品,在\(n\)个地点的价格为\(a_i\),现在一次经过这\(n\)个地点,在每个地点可以买一个这样的物品,也可以卖出一个物 ...

  6. 2019-ACM-ICPC-南京区网络赛-D. Robots-DAG图上概率动态规划

    2019-ACM-ICPC-南京区网络赛-D. Robots-DAG图上概率动态规划 [Problem Description] ​ 有向无环图中,有个机器人从\(1\)号节点出发,每天等概率的走到下 ...

  7. HDU 5880 Family View (2016 青岛网络赛 C题,AC自动机)

    题目链接  2016 青岛网络赛  Problem C 题意  给出一些敏感词,和一篇文章.现在要屏蔽这篇文章中所有出现过的敏感词,屏蔽掉的用$'*'$表示. 建立$AC$自动机,查询的时候沿着$fa ...

  8. The 2018 ACM-ICPC Asia Qingdao Regional Contest(青岛网络赛)

    A Live Love 水 #include <algorithm> #include<cstdio> #include<cstring> using namesp ...

  9. 计蒜客 2018南京网络赛 I Skr ( 回文树 )

    题目链接 题意 : 给出一个由数字组成的字符串.然后要你找出其所有本质不同的回文子串.然后将这些回文子串转化为整数后相加.问你最后的结果是多少.答案模 1e9+7 分析 : 应该可以算是回文树挺裸的题 ...

随机推荐

  1. jvm编译环境搭建 Debina篇

    这里参考了 <Java虚拟机精讲> <深入理解Java虚拟机 JVM高级特性与最佳实践> http://www.cnblogs.com/zxfdream/p/5411511.h ...

  2. 签名Android应用程序

    Android要求对作为产品发布的应用进行签名(包名相同的化,后安装的应用会覆盖前面安装的应用) 签名作用:1.确定发布者的身份.2.确保应用的完整性. 注意:在应用的开发.调试阶段,Eclipse的 ...

  3. (转)【经验之谈】Git使用之TortoiseGit配置VS详解

    原文地址:http://www.cnblogs.com/xishuai/p/3590705.html 前言 上一篇<[经验之谈]Git使用之Windows环境下配置>: 安装 配置和使用 ...

  4. git fatal:HttpRequestException encountered

    网上查了一下发现是Github 禁用了TLS v1.0 and v1.1,必须更新Windows的git凭证管理器,才行. https://github.com/Microsoft/Git-Crede ...

  5. Linux 基础教程 36-查看系统性能

    uptime     uptime命令功能比较简单,主要功能如下所示: 查看服务器的开机时长 查看CPU负载 基本用法 uptime 用法示例 [root@localhost ~]# uptime 1 ...

  6. c+内存管理机制

    内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的 检查代码和对C++的痛恨,但内存管理在C++中无处不在,内存 ...

  7. Bootstrap轮播

    实现原理:隐藏所有要显示的元素,然后指定要显示的为block,宽.高自适应. 结构: 容器:最外层为一个div 使用data-ride="carousel" 来指定为轮播插件.并提 ...

  8. 自我介绍及如何注册GITHUB

    自我介绍 我是来自南通大学网络工程141班的周楠,我的学号是1413042014,我的兴趣是喜欢玩游戏(如果这算是一个兴趣爱好的话),喜欢尝试各种游戏. 如何注册一个GitHub账号? 1.首先我们需 ...

  9. Github的注册经历

    姓名 韦军 学号 1413042023 班级 网络141 兴趣爱好 读书 上网 在注册Github时,先去网上下载了一个Github的app,打开一看全是英文,还是看懂了一些,点击开始注册,在注册时还 ...

  10. 史上最详细的Hadoop环境搭建(转)

    转载的文章,请告知侵删.本人只是做个记录,以免以后找不到. 前言 Hadoop在大数据技术体系中的地位至关重要,Hadoop是大数据技术的基础,对Hadoop基础知识的掌握的扎实程度,会决定在大数据技 ...