题解 P2617 【Dynamic Ranking】
这道题目是一道整体二分的好题
想必做这道题目的人都知道二分答案,而整体二分相当于一次性二分所有答案,很显然可以发现,对于这一道题目而言,时间复杂度是m log 1e9,是可以接受的
对于每一次修改,我们可以考虑:减去原来的值,加上修改成的值,包括读入原先序列ai也可以是如此。
还有一个要点是统计mb的排名。最暴力的方式是开一个计数表去一个个数过来,但是我们可以想一下,比如mb=50,前面有两个数,一个a1=4,一个a2=49,那么这两个数的值是有必要考虑的吗?显然没有必要,因为它们都比50小,因此我们可以把它们都当成两个值是1的标记,接下来统计排名只要计算前缀和就可以了。因此这道题目可以用树状数组或者线段树进行维护。
还有一个小要点,就是对于脏数据的处理。脏数据也就是会对计算结果产生影响的数据。对于本题而言,值大于等于mb的操作在[lb,mb)中是没有影响的,但是在[mb,ub)的操作中,值小于mb的操作会产生影响,但是显然如果减去一个k,那么就可以消除这个影响。
其实这个程序有很有趣的一点。这道题的原题是zoj2112,在大学OJ里面是有多组数据的。这个程序几乎可以不用初始化直接提交过去,原因就是每一组数据整体二分之后,所有参与的数组的值全部都空了。具体可以自己证明一下
代码如下:
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
struct op
{
int type;
//type==0 Change i means position; j means ispositive; k means the number after change
//type==1 Query i means left; r means right; k means kth-number
int i,j,k;
int id;
};
int n,m,a[10050],ans[10050],f[50050];
vector <op> q;
int lowbit(int x)
{
return x&-x;
}
void add(int x,int k)
{
while (x<=n)
{
f[x]+=k;
x+=lowbit(x);
}
}
int query(int k)
{
int ans=0;
while (k>0)
{
ans+=f[k];
k-=lowbit(k);
}
return ans;
}
void solve(int lb,int ub,vector <op> &q)
{
vector <op> Left;
vector <op> Right;
int mb=(lb+ub)>>1;
//cout << lb << " " << mb << " " << ub << endl;
if (ub-lb==1)
{
for (int i=0;i<q.size();i++)
{
if (q[i].type==1)
ans[q[i].id]=lb;
}
return;
}
else if (q.empty())
return;
for (int i=0;i<q.size();i++)
{
op tmp=q[i];
if (tmp.type==0)
{
if (tmp.k<mb)
{
add(tmp.i,tmp.j);//i:pos j:num
Left.push_back(tmp);
}
else
Right.push_back(tmp);
}
else
{
int kth=query(tmp.j)-query(tmp.i-1);
if (kth>=tmp.k)
Left.push_back(tmp);
else
{
tmp.k-=kth;
Right.push_back(tmp);
}
}
}
for (int x=0;x<Left.size();x++)
if(Left[x].type==0)
add(Left[x].i,-Left[x].j);
solve(lb,mb,Left);
solve(mb,ub,Right);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
op tmp={0,i,1,a[i]};
q.push_back(tmp);
}
for (int x=0;x<m;x++)
{
char cmd;
int i;
scanf("%s%d",&cmd,&i);
if (cmd=='C')
{
int t;
scanf("%d",&t);
op tmp={0,i,-1,a[i],0};
q.push_back(tmp);
a[i]=t;
tmp={0,i,1,t,0};
q.push_back(tmp);
}
else
{
int j,k;
scanf("%d%d",&j,&k);
op tmp={1,i,j,k,x};
q.push_back(tmp);
}
}
for (int i=0;i<m;i++)
ans[i]=-1;
solve(0,1e9,q);
for (int i=0;i<m;i++)
if (ans[i]!=-1)
printf("%d\n",ans[i]);
return 0;
}
然而整体二分还是比树套树慢,但是整体二分的优势就是好写。特别对于我这种弱省蒟蒻而言。