这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
2020-2021:teams:legal_string:jxm2001:contest:cf_728_div._1 [2021/07/06 10:29] jxm2001 |
2020-2021:teams:legal_string:jxm2001:contest:cf_728_div._1 [2021/07/09 20:24] (当前版本) jxm2001 |
||
---|---|---|---|
行 1: | 行 1: | ||
- | ====== Codeforces Round #706 (Div. 1) ====== | + | ====== Codeforces Round #728 (Div. 1) ====== |
[[http://codeforces.com/contest/1540|比赛链接]] | [[http://codeforces.com/contest/1540|比赛链接]] | ||
行 117: | 行 117: | ||
设 $\text{dp}(i,s)$ 表示 $\sum_{j=1}^i a_i=s$ 的情况,$m=\max c_i$,利用差分可以 $O(n^2m)$ 计算答案。 | 设 $\text{dp}(i,s)$ 表示 $\sum_{j=1}^i a_i=s$ 的情况,$m=\max c_i$,利用差分可以 $O(n^2m)$ 计算答案。 | ||
- | 最后有 $\min\left(\cfrac {-ssb_i}i\right)\le f_1\le \min\left(\cfrac {sc_i-ssb_i}i\right)$,于是有 $\max(f_1)-\min f_1\le m$。 | + | 最后有 $\min\left(\cfrac {-ssb_i}i\right)\le f_1\le \min\left(\cfrac {sc_i-ssb_i}i\right)$,于是有 $\max(f_1)-\min (f_1)\le m$。 |
即有效的 $x$ 只有 $O(m)$ 个,最终时间复杂度 $O\left(n^2m^2\right)$。 | 即有效的 $x$ 只有 $O(m)$ 个,最终时间复杂度 $O\left(n^2m^2\right)$。 | ||
行 168: | 行 168: | ||
x=min(max(lef,x),rig+1); | x=min(max(lef,x),rig+1); | ||
enter(ans[x-lef]); | enter(ans[x-lef]); | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | ===== D. Inverse Inversions ===== | ||
+ | |||
+ | ==== 题意 ==== | ||
+ | |||
+ | 现有一个 $1\sim n$ 的排列,设 $p_i$ 表示元素 $i$ 在排列中的位置。已知序列 $b$,其中 $b_i=\sum_{j=1}^i[p_j\gt p_i]$。 | ||
+ | |||
+ | 接下来两种操作: | ||
+ | |||
+ | - 修改某个 $b_x$ | ||
+ | - 询问某个 $p_x$ | ||
+ | |||
+ | ==== 题解 ==== | ||
+ | |||
+ | 设 $P(i,r)=\sum_{j=1}^r[p_j\gt p_i]$,$c_i=i-1-b_i$,于是有 $P(i,i)=c_i,P(i,n)=p_i-1$。 | ||
+ | |||
+ | 不难发现 $P(i,r+1)=P(i,r)+[c_{r+1}\le P(i,r)]$。 | ||
+ | |||
+ | 对修改操作仅维护 $c_i$,时间复杂度 $O(1)$。对询问操作直接暴力转移,时间复杂度 $O(n)$。 | ||
+ | |||
+ | 考虑分块,设分块大小为 $B$。对每个块 $[l_i,r_i]$,考虑线段树维护 $P(x,l_i)(l_i\le x\le r_i)$。 | ||
+ | |||
+ | 对线段树的每个区间 $[L,R]$,维护 $P(x,R)(L\le x\le R)$,于是 $\text{push_up}$ 操作可以双指针维护,时间复杂度 $O(R-L)$。 | ||
+ | |||
+ | 修改某个 $b_x$ 对应线段树的单点修改操作,于是修改操作的总复杂度 $O(B)$。 | ||
+ | |||
+ | 对于查询操作,设 $x\in [l_i,r_i]$,可以先 $O(B)$ 暴力计算出 $P(x,r_i)$。 | ||
+ | |||
+ | 然后对后续每个块 $k$,利用序列 $P(x,r_k)$ 可以二分计算贡献,时间复杂度 $O(\frac nB\log B)$。 | ||
+ | |||
+ | 不难发现取 $B=O(\sqrt{n\log n})$ 时复杂的最小,此时时间复杂度为 $O\left(q\sqrt{n\log n}\right)$,空间复杂度 $O(n\log n)$。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXN=1e5+5,MAXB=600; | ||
+ | int b[MAXN]; | ||
+ | struct Tree{ | ||
+ | int lef[MAXB<<2],rig[MAXB<<2]; | ||
+ | vector<int> p[MAXB<<2]; | ||
+ | void push_up(int k){ | ||
+ | int pos1=0,pos2=0,k1=k<<1,k2=k<<1|1; | ||
+ | p[k].clear(); | ||
+ | _rep(i,lef[k],rig[k]){ | ||
+ | if(pos1<p[k1].size()&&pos2<p[k2].size()){ | ||
+ | if(p[k1][pos1]+pos2<p[k2][pos2]) | ||
+ | p[k].push_back(p[k1][pos1++]+pos2); | ||
+ | else | ||
+ | p[k].push_back(p[k2][pos2++]); | ||
+ | } | ||
+ | else if(pos1<p[k1].size()) | ||
+ | p[k].push_back(p[k1][pos1++]+pos2); | ||
+ | else | ||
+ | p[k].push_back(p[k2][pos2++]); | ||
+ | } | ||
+ | } | ||
+ | void build(int k,int L,int R){ | ||
+ | lef[k]=L,rig[k]=R; | ||
+ | int M=L+R>>1; | ||
+ | if(L==R){ | ||
+ | p[k].push_back(b[M]); | ||
+ | return; | ||
+ | } | ||
+ | build(k<<1,L,M); | ||
+ | build(k<<1|1,M+1,R); | ||
+ | push_up(k); | ||
+ | } | ||
+ | void update(int k,int pos){ | ||
+ | if(lef[k]==rig[k]){ | ||
+ | p[k][0]=b[pos]; | ||
+ | return; | ||
+ | } | ||
+ | int mid=lef[k]+rig[k]>>1; | ||
+ | if(pos<=mid) | ||
+ | update(k<<1,pos); | ||
+ | else | ||
+ | update(k<<1|1,pos); | ||
+ | push_up(k); | ||
+ | } | ||
+ | int query(int v){ | ||
+ | int lef=1,rig=p[1].size(),ans=0; | ||
+ | while(lef<=rig){ | ||
+ | int mid=lef+rig>>1; | ||
+ | if(v+mid>p[1][mid-1]){ | ||
+ | ans=mid; | ||
+ | lef=mid+1; | ||
+ | } | ||
+ | else | ||
+ | rig=mid-1; | ||
+ | } | ||
+ | return ans; | ||
+ | } | ||
+ | }tree[MAXB]; | ||
+ | int blk_id[MAXN],lef[MAXB],rig[MAXB]; | ||
+ | int main() | ||
+ | { | ||
+ | int n=read_int(),blk_sz=sqrt(n*log(n))/2+1,m=n/blk_sz; | ||
+ | if(n%blk_sz)m++; | ||
+ | _rep(i,1,n)b[i]=i-1-read_int(); | ||
+ | _for(i,0,m){ | ||
+ | lef[i]=i*blk_sz+1; | ||
+ | rig[i]=min((i+1)*blk_sz,n); | ||
+ | _rep(j,lef[i],rig[i]) | ||
+ | blk_id[j]=i; | ||
+ | tree[i].build(1,lef[i],rig[i]); | ||
+ | } | ||
+ | int q=read_int(); | ||
+ | while(q--){ | ||
+ | int opt=read_int(),x=read_int(); | ||
+ | if(opt==1){ | ||
+ | b[x]=x-1-read_int(); | ||
+ | tree[blk_id[x]].update(1,x); | ||
+ | } | ||
+ | else{ | ||
+ | int ans=b[x]; | ||
+ | _rep(i,x+1,rig[blk_id[x]]){ | ||
+ | if(ans>=b[i]) | ||
+ | ans++; | ||
+ | } | ||
+ | _for(i,blk_id[x]+1,m) | ||
+ | ans+=tree[i].query(ans); | ||
+ | enter(ans+1); | ||
+ | } | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | 另外附上一份时间复杂度 $O\left(q\sqrt nlog n\right)$,空间复杂度 $O(n)$ 的代码,树状数组上二分写的。 | ||
+ | |||
+ | <hidden 查看代码> | ||
+ | <code cpp> | ||
+ | const int MAXN=1e5+5; | ||
+ | #define lowbit(x) ((x)&(-x)) | ||
+ | int c[MAXN]; | ||
+ | void add(int pos,int v){ | ||
+ | while(pos<MAXN){ | ||
+ | c[pos]+=v; | ||
+ | pos+=lowbit(pos); | ||
+ | } | ||
+ | } | ||
+ | int query(int b){ | ||
+ | int pos=0,curb=0; | ||
+ | for(int i=16;i>=0;i--){ | ||
+ | if(pos+(1<<i)<MAXN&&c[pos+(1<<i)]+(1<<i)+curb<b){ | ||
+ | curb+=c[pos+(1<<i)]+(1<<i); | ||
+ | pos+=(1<<i); | ||
+ | } | ||
+ | } | ||
+ | return pos+1; | ||
+ | } | ||
+ | int b[MAXN],lef[MAXN],rig[MAXN],blk_id[MAXN]; | ||
+ | vector<int> a[MAXN]; | ||
+ | void build(int k){ | ||
+ | a[k].clear(); | ||
+ | _rep(i,lef[k],rig[k]){ | ||
+ | int t=query(b[i]+1)-1; | ||
+ | a[k].push_back(t); | ||
+ | add(t+1,1); | ||
+ | } | ||
+ | sort(a[k].begin(),a[k].end()); | ||
+ | _for(i,0,a[k].size()) | ||
+ | add(a[k][i]+1,-1); | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | int n=read_int(),blk_sz=sqrt(n/2)+1,m=n/blk_sz; | ||
+ | if(n%blk_sz)m++; | ||
+ | _rep(i,1,n)b[i]=read_int(); | ||
+ | _for(i,0,m){ | ||
+ | lef[i]=i*blk_sz+1; | ||
+ | rig[i]=min((i+1)*blk_sz,n); | ||
+ | _rep(j,lef[i],rig[i]) | ||
+ | blk_id[j]=i; | ||
+ | build(i); | ||
+ | } | ||
+ | int q=read_int(); | ||
+ | while(q--){ | ||
+ | int opt=read_int(),x=read_int(); | ||
+ | if(opt==1){ | ||
+ | b[x]=read_int(); | ||
+ | build(blk_id[x]); | ||
+ | } | ||
+ | else{ | ||
+ | int ans=b[x]; | ||
+ | _rep(i,x+1,rig[blk_id[x]]){ | ||
+ | if(ans>=b[i]) | ||
+ | ans++; | ||
+ | } | ||
+ | _for(i,blk_id[x]+1,m) | ||
+ | ans+=upper_bound(a[i].begin(),a[i].end(),ans)-a[i].begin(); | ||
+ | enter(n-ans); | ||
+ | } | ||
} | } | ||
return 0; | return 0; |