首先我们先说说3295题的树套树做法:显然只需要求出每次修改x左边比自身大的数的个数和右边比自身小的个数,就是x的贡献,每次减去贡献再删除x即可,这样就相当于带修改的可持久化线段树就能解决问题。显然,我们用树状数组套权值线段树来解决这个问题,每次修改就相当于修改棵可持久化线段树上的一个权值,所以在修改上总的时间复杂度是,下面来考虑查询。
我们每次查询显然可以分成段,这样我们显然可以来完成所有的查询。
然后考虑空间问题。采用动态开点的方法可以将总的空间复杂度压缩到级别,这是由于每个点至多影响个点,这样我们可以仅仅开3000万个数的空间。如果你还是怕出问题,可以用一个unsigned short和一个char来压8位的方法解决问题。总时间复杂度
这样我们就能解决这个问题了。
那么用CDQ分治怎么解决这个问题呢?首先我们考虑每个数对逆序对的贡献f[],那么我们可以猜想下一次的答案nextans满足: nextans=ans-f[i] 但是显然我们可以发现这个猜想是错误的,因为删除的数可能贡献了重复的逆序对,因此我们只要求出删除的数按照原位置的逆序对组数就可以了。
那么我们怎么求这个组数呢。首先,若x[],y[],pos[]分别代表一个数的值,位置和删除时间,g[i]代表加入删去第i个数后逆序对增加的数量,那么有
显然我们仅仅按pos排序是搞不出什么名堂的qaq,因为是二维,所以不好处理(似乎这里又可以回归树套树套路),这时我们可以考虑cdq分治套路,递归将询问按y值排序,这样我们用分治法,先求出仅在i,j<=mid的答案,再求出i,j>mid的答案,然后求出i<mid,j<mid的答案即可。
于是我们可以树状数组维护一下,按照y值顺序依次加入,然后依次query一下就可以了(细节我就不说了),于是我们愉快地艹掉了一维,时间复杂度
具体可以看代码,写了无数的注释之后发现我zz地把归并写错辣
#include"bits/stdc++.h"
using namespace std;
const int N=300005;
typedef long long ll;
const int T=1500005;
char _buff[T];int _pos;
void read(){fread(_buff,T,1,stdin);}
#define gch _buff[_pos++]
template<class integer>
inline void read(integer&x){
x=0;int f=1;char ch=gch;
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=gch;}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gch;}
x*=f;
}
#define lowbit(x) ((x)&-(x))
int a[N],q[N],n,m;
int sum[N],f[N],g[N],num[N];
ll ans;
inline void add(int x,int u){
for(num[x]+=u;x<=n;x+=lowbit(x))
sum[x]+=u;
}
inline int qry(int l,int r){
int re=0;
while(r>=l){
for(;r-lowbit(r)+1>=l && r;r-=lowbit(r))
re+=sum[r];
if(r>=l) re+=num[r--];
}
return re;
}
void init(){
read();read(n),read(m);int i;
for(i=1;i<=n;i++)read(a[i]);for(i=1;i<=m;i++)read(q[i]);
for(i=1;i<=n;i++)f[i]=qry(a[i]+1,n),add(a[i],1);
for(i=1;i<=n;i++)add(a[i],-1),f[i]+=qry(1,a[i]-1);
}
int qu[N],b[N];
//计算每次增加数时的逆序对总数:
//每个数有三个属性:值 位置 问题时间
//即 a[qu[]] qu[] pos 用x,y,z表示
//则i和j满足逆序对的充分必要条件为 (xi-xj) * (yi-yj) < 0 此时在max(zi,zj)会构成逆序对
void solve(int l,int r){
if(l==r){g[qu[l]]=0;return;}
//当l==r说明无法分治,显然这一段不存在逆序对
int mid=(l+r)>>1,i,j;
solve(l,mid);solve(mid+1,r);
//分治处理i和j均在左边,均在右边的情况
//假设j在左侧,i在右侧
//处理qu[j]<qu[i] a[qu[j]]>a[qu[i]]的情况
for (j=l,i=mid+1; i<=r; i++){ //枚举右侧每一个值
while (j<=mid && qu[j]<qu[i])
add(a[qu[j]],1),j++; //由于按照qu排序,处理qu[j]<qu[i]的情况
g[qu[i]] += qry(a[qu[i]]+1,n); //求这种情况的总数
}
while (j<=mid) add(a[qu[j]],1),j++; //加上剩余部分的值即加上了所有左侧值
//处理qu[j]>qu[i] a[qu[j]]<a[qu[i]]的情况
for (j=l,i=mid+1; i<=r; i++){ //仍然枚举右侧每一个值
while (j<=mid && qu[j]<qu[i])
add(a[qu[j]],-1),j++; //删除qu[j]<qu[i]的情况
g[qu[i]] += qry(1,a[qu[i]]-1); //求这种情况的总数
}
while (j<=mid) add(a[qu[j]],-1),j++; //减去剩余的情况
for (j=l,i=mid+1; i<=r||j<=mid; ){
if (j<=mid && (i>r || qu[j]<qu[i])) b[i-mid+j-1]=qu[j],j++;
else b[i-mid+j-1]=qu[i],i++;
}
for (i=l; i<=r; i++)
qu[i] = b[i];
}
int p[N];
int main(){
init(); int i;
for(i=1;i<=n;i++)p[a[i]]=i;
for(i=1;i<=m;i++)qu[i]=p[q[i]];
solve(1,m);
for(i=1;i<=n;i++)ans+=f[i];
for(i=1,ans>>=1;i<=m;i++){
printf ("%lld\n",ans);
ans += - f[p[q[i]]] + g[p[q[i]]];
}
return 0;
}
整体二分感觉和CDQ分治其实差不多,对于2527这道题,显然如果对于单个的国家我们可以二分答案然后求值,但是如果这样的话时间复杂度就会愉快地达到级别,显然作死。但是我们同样可以利用分治思想整体处理问题,首先我们将答案>mid和<=mid的分成两部分,显然>mid的一部分可以借用之前的判断答案与mid关系时计算出的值。那么这样分治下去的复杂度是多少呢?我们来分析一下。
我们分层考虑,对于每一层都有n个站,共有logn层。每一层进行的操作的次数都是级别的,那么我们的时间复杂度就有
#include"bits/stdc++.h"
using namespace std;
const int N=300005;
typedef long long ll;
const int T=40000005;
char _buff[T];int _pos;
void read(){fread(_buff,T,1,stdin);}
#define gch _buff[_pos++]
template<class integer>
inline void read(integer&x){
x=0;int f=1;char ch=gch;
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=gch;}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gch;}
x*=f;
}
int L[N*2],R[N*2],val[N*2],id[N],ex[N],ans[N],n,m,q;
int h[N],nxt[N];ll add[N],num[N],sum[N];
void init(){
read();read(m),read(n);
int i;for(i=1;i<=n;i++)read(id[i]);
for(i=1;i<=m;i++)read(ex[i]);read(q);
for(i=1;i<=q;i++)read(L[i]),read(R[i]),read(val[i]);
for(i=1;i<=n;i++)nxt[i]=h[id[i]],h[id[i]]=i;
}
int qu[N],done;
ll tmp[N];
#define lowbit(x) ((x)&-(x))
void up(int l,int r,int u){
while (r>=l){
num[r]+=u; r--;
for (; r-lowbit(r)+1>=l && r; r-=lowbit(r))
add[r]+=u;
}
}
void clear(int del_first){
while (done >= del_first){
if (L[done] <= R[done])
up (L[done], R[done], -val[done]);
else up (L[done], n, -val[done]),
up (1, R[done], -val[done]);
done -- ;
}
}
ll get(int x){
ll re = num[x];
for (; x<=n; x+=lowbit(x))
re += add[x];
return re;
}
void work(int l,int r,int&d,int ncase){
int i,j;
while(done<min(ncase,q)){
done ++ ;
if (L[done] <= R[done])
up (L[done], R[done], val[done]);
else up (L[done], n, val[done]),
up (1, R[done], val[done]);
}
for (i=l; i<=r; i++){
for (j=h[qu[i]]; j; j=nxt[j]){
tmp[qu[i]] += get(j);
if(tmp[qu[i]] > 1e18)
break;
}
}
for (i=j=l; j<=r; j++)
if (tmp[qu[j]] >= ex[qu[j]])
swap(qu[i],qu[j]),i++;
d=i-1;
for (i=l; i<=r; i++) tmp[qu[i]]=0;
}
void solve(int s,int t,int l,int r){
if(l==r){for(int i=s;i<=t;i++)ans[qu[i]]=l;return;}
int mid=(l+r)>>1,p,i;work(s,t,p,mid);
if(p<t)solve(p+1,t,mid+1,r);clear(l);
if(s<=p)solve(s,p,l,mid);
}
int main(){
init();int i;for(i=1;i<=m;i++)qu[i]=i;solve(1,m,1,q+1);
for(i=1;i<=m;i++)ans[i]>q?puts("NIE"):printf("%d\n",ans[i]);
return 0;
}