Part 0
这道题其实就是一个码农题,考的是代码功底,没有太多思维难点。
注意题目的HINT说的请认真阅读题面,你会发现 c ≤ 7 c \leq 7 c≤7然后再看一下询问。
剩下唯一的难点只剩下维护一个联通块内所有节点权值的积。首先,要知道以下公式。
ln a b = ln a + ln b \ln ab = \ln a + \ln b lnab=lna+lnb
ln \ln ln函数是单调的,故判断两个数积的大小就可以判断两个数的自然对数的大小。
3,4操作都可已用懒惰标记来解决。
AC代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define M 400005
using namespace std;
int tot,n;
int C[M];
double D[M];
int Get_d(int x){return lower_bound(C+1,C+tot+1,x)-C;}
struct ST{
int tot;
int Root[M],Lson[M*12],Rson[M*12];
int cnt[M*12];
double sum[M*12];
bool CL[M*12];//懒惰标记
ST(){
tot=0;
Root[0]=Lson[0]=Rson[0]=cnt[0]=sum[0]=CL[0]=0;
memset(Root,0,sizeof(Root));
}
void Create(int &tid){
tid=++tot;
Lson[tid]=Rson[tid]=cnt[tid]=sum[tid]=CL[tid]=0;
}
void Up(int tid){
cnt[tid]=cnt[Lson[tid]]+cnt[Rson[tid]];
sum[tid]=sum[Lson[tid]]+sum[Rson[tid]];
}
void Down(int tid){
if(!CL[tid])return;
if(Lson[tid]){
sum[Lson[tid]]=0;
cnt[Lson[tid]]=0;
CL[Lson[tid]]=1;
}
if(Rson[tid]){
sum[Rson[tid]]=0;
cnt[Rson[tid]]=0;
CL[Rson[tid]]=1;
}
CL[tid]=0;
}
void Updata(int L,int R,int x,int d,int &tid){
if(tid==0)Create(tid);
cnt[tid]+=d;
sum[tid]+=(double)d*D[x];
if(L==R)return;
int mid=(L+R)>>1;
Down(tid);
if(x<=mid)Updata(L,mid,x,d,Lson[tid]);
else Updata(mid+1,R,x,d,Rson[tid]);
}
int Change(int L,int R,int Lx,int Rx,int tid){
if(Rx<Lx)return 0;
if(Lx<=L&&R<=Rx){
int res=cnt[tid];
if(tid){//清空当前节点的信息
cnt[tid]=0;
sum[tid]=0;
CL[tid]=1;
}
return res;
}
int mid=(L+R)>>1;
Down(tid);
int res=0;
if(Rx<=mid)res=Change(L,mid,Lx,Rx,Lson[tid]);
else if(Lx>mid)res=Change(mid+1,R,Lx,Rx,Rson[tid]);
else res=Change(L,mid,Lx,mid,Lson[tid])+Change(mid+1,R,mid+1,Rx,Rson[tid]);
Up(tid);
return res;
}
int Find(int L,int R,int k,int tid){
if(L==R)return C[L];
int res=cnt[Lson[tid]];
int mid=(L+R)>>1;
Down(tid);
if(res>=k)return Find(L,mid,k,Lson[tid]);
else return Find(mid+1,R,k-res,Rson[tid]);
}
void Merge(int L,int R,int &x,int y){
if(!x||!y)return (void)(x+=y);
Down(x),Down(y);
int mid=(L+R)>>1;
if(L==R){
cnt[x]+=cnt[y];
sum[x]+=sum[y];
return;
}
Merge(L,mid,Lson[x],Lson[y]);
Merge(mid+1,R,Rson[x],Rson[y]);
Up(x);
}
bool cmp(int x,int y){
return sum[Root[x]]>sum[Root[y]];
}
int get_sz(int x){return cnt[Root[x]];}
}ST;
int fa[M];
int getfa(int v){//并查集
if(fa[v]==v)return v;
return fa[v]=getfa(fa[v]);
}
struct Que{
int op,a,x,id;
void Read(){
scanf("%d",&op);
switch (op){
case 1:{
scanf("%d",&x);
C[++tot]=x;
id=++n;//节点编号
break;
}
case 2:scanf("%d%d",&a,&x);break;
case 3:{
scanf("%d%d",&a,&x);
C[++tot]=x;
break;
}
case 4:{
scanf("%d%d",&a,&x);
C[++tot]=x;
break;
}
case 5:scanf("%d%d",&a,&x);break;
case 6:scanf("%d%d",&a,&x);break;
case 7:scanf("%d",&a);break;
case 8:scanf("%d%d",&a,&x);break;//可忽略
case 9:scanf("%d",&a);break;
}
}
void solve(){
switch (op){
case 1:{
int tid=Get_d(x);
ST.Updata(1,tot,tid,1,ST.Root[id]);
break;
}
case 2:{
int X=getfa(a),Y=getfa(x);
if(X==Y)break;
fa[Y]=X;
ST.Merge(1,tot,ST.Root[X],ST.Root[Y]);//合并两个联通块的信息
break;
}
case 3:{
int X=getfa(a),d=Get_d(x);
int res=ST.Change(1,tot,1,d-1,ST.Root[X]);//把1~d-1的值都变为0,并统计个数
ST.Updata(1,tot,d,res,ST.Root[X]);
break;
}
case 4:{
int X=getfa(a),d=Get_d(x);
int res=ST.Change(1,tot,d+1,tot,ST.Root[X]);//与操作3类似
ST.Updata(1,tot,d,res,ST.Root[X]);
break;
}
case 5:{
int X=getfa(a);
printf("%d\n",ST.Find(1,tot,x,ST.Root[X]));
break;
}
case 6:{
int X=getfa(a),Y=getfa(x);
printf("%d\n",ST.cmp(X,Y));
break;
}
case 7:{
int X=getfa(a);
printf("%d\n",ST.get_sz(X));
break;
}
}
}
}Q[M];
void Init(){
for(int i=1;i<=n;i++)fa[i]=i;
sort(C+1,C+tot+1);
tot=unique(C+1,C+tot+1)-C-1;
for(int i=1;i<=tot;i++)D[i]=log(C[i]);//将数值转换
}
int main(){
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)Q[i].Read();
Init();
for(int i=1;i<=m;i++)Q[i].solve();
return 0;
}