洛谷.3960.列队(线段树/树状数组)
线段树做法:
先考虑一行的情况。每次操作为在序列中取出一个元素,然后放到取完后的第n个位置。如果我们预留n+q个位置,那这个操作就相当于线段树单点删除和单点加入。
扩展到多行。我们发现对最后一列的操作和对某一行的操作是相同的(取出一个,放到末尾一个)。
于是我们可以用n+1棵动态开点线段树维护。
有些细节:比如删除后不需要真插到线段树里。。用vector即可。
树状数组做法:
基本思路不变(一维扩展到多维,最后一列单独维护)。
对于每一行(除最后一列),有影响的只是对这一行的操作。
那么对于某一行,可以算出某次操作删掉的是第几次补在这一行后的数(或者是原数列中的哪个数)。同样是查k大值计算。
因为总共只会有q个补在某行后面的数,可以直接用vector存每一行每次补在后面的数。最后一列一样。
求k大值(第k个存在的数)线段树平衡树都行。二分+树状数组也可以。
因为不能动态开点开n+1棵树状数组,所以离线,每次对每一行用树状数组求,然后改初始状态。
最后对最后一列也求一遍即可。
复杂度\(O(n\log^2n)\)。但常数小啊。(但还是比线段树慢?)
我竟然用边表存每一行的询问mdzz(顺序会反)。
至于平衡树。。真心不想再写它了。
线段树:
//1736ms 54284kb
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 150000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=3e5+4;
int n,m,R,root[N];
std::vector<LL> v[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define S N*18//*2
#define lson son[x][0],l,m
#define rson son[x][1],m+1,r
int tot,son[S][2],sum[S];//不需要直接记区间点数啊== 记删除点数就行
#undef S
int Delete(int &x,int l,int r,int p)
{
if(!x) x=++tot; ++sum[x];
if(l==r) return l;
int m=l+r>>1,tmp=m-l+1-sum[son[x][0]];
if(tmp<p) return Delete(rson,p-tmp);
return Delete(lson,p);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
LL Solve1(int x,LL tmp)
{
int p=T.Delete(root[0],1,R,x);
LL ans= p<=n?(LL)p*m:v[0][p-n-1];
v[0].push_back(tmp?tmp:ans);
return ans;
}
LL Solve2(int x,int y)
{
int p=T.Delete(root[x],1,R,y);
LL ans= p<m?(1ll*(x-1)*m+p):v[x][p-m];
v[x].push_back(Solve1(x,ans));
return ans;
}
int main()
{
n=read(),m=read(); int Q=read(); R=std::max(n,m)+Q;
for(int x,y; Q--; )
{
x=read(),y=read();
if(y==m) printf("%lld\n",Solve1(x,0));
else printf("%lld\n",Solve2(x,y));
}
return 0;
}
树状数组:
//1951ms 32664kb
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=3e5+5;
int qx[N],qy[N],pos[N];
std::vector<LL> e[N],v[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct BIT
{
int n,t[N*4];//2n! 再开一倍因为预处理
#define lb(x) (x&-x)
inline void Add(int p)
{
for(; p<=n; p+=lb(p)) ++t[p];
}
inline void Delete(int p)
{
for(; p<=n; p+=lb(p)) --t[p];
}
inline int Query(int p)
{
int res=0;
for(; p; p^=lb(p)) res+=t[p];
return res;
}
void Init(int nn)
{
n=nn, ++t[n];
for(int i=1; i<n; ++i) t[i+lb(i)]+=++t[i];//init:t[i]=1 i+lb(i)>n
}
inline int Kth(int k)
{
int l=1,r=n,mid;
while(l<r)
if(Query(mid=l+r>>1)<k) l=mid+1;
else r=mid;
return l;
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
int main()
{
int n=read(),m=read(),q=read();
for(int i=1; i<=q; ++i)
{
qx[i]=read(),qy[i]=read();
if(qy[i]!=m) e[qx[i]].push_back(i);
}
T.Init(std::max(n,m)+q);
for(int x=1; x<=n; ++x)
{
for(int i=0,j,l=e[x].size(); i<l; ++i)
j=e[x][i], T.Delete(pos[j]=T.Kth(qy[j]));
for(int i=0,l=e[x].size(); i<l; ++i) T.Add(pos[e[x][i]]);
}
for(int i=1,x,y,p; i<=q; ++i)
{
x=qx[i],y=qy[i];
T.Delete(p=T.Kth(x));
LL ans= p<=n?(LL)p*m:v[0][p-n-1];
if(y!=m)
{
v[x].push_back(ans);
ans= pos[i]<m?(1ll*(x-1)*m+pos[i]):v[x][pos[i]-m];
}
v[0].push_back(ans);
printf("%lld\n",ans);
}
return 0;
}
------------------------------------------------------------------------------------------------------------------------
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------