http://acm.hdu.edu.cn/showproblem.php?pid=6756
比赛时用的对询问分块,赛后测数据要跑10s,卡这题卡了巨久,和队友写了两份代码都T,没时间看其他题了。。。以前写过一些题,就是把修改放在一个修改队列里面,大于sqrt(q)个的时候重构整个某数据结构,于是询问是q*sqrt(q)的,重构时n*sqrt(q)的。
这题就对每个点开个动态开点线段树或者树状数组,由于mex值最大为du[i],所以只要开du[i]的长度,空间是构的。然后把修改放到修改队列里面,不修改a[i]的值。到了询问的时候,去遍历修改队列里面有没有对他有影响的,就去改一下那个点的线段树里的值,然后线段树上二分求出mex值。如果修改队列大于了sqrt(q),那么把修改队列中的所有值更新到a数组中,顺便重构所有的点的线段树。
复杂度是一样的,不过上述算法确实常数很大。。。这n,m,q=1e5,t=10着实顶不住,要是5e4,t=3海星。。。
于是用了题解的对度数分块过了
我们考虑小点和大点,小点询问,直接暴力找旁边所有点的a,小点更新,那么只需要把相邻的大点的树状数组给更新了,大点更新,只更新a[u],大点询问u,首先把相邻的大点v都拿出来,看要不要更新u的树状数组,因为小点已经更新过了,没更新的只有大点的修改,所以不用考虑。
注意考虑重边情况,一开始没去重,wa了一年,然后去重了过了,然而改正错误发现不去重会T掉
#include<bits/stdc++.h>
using namespace std;
const int maxl=1e5+10;
int n,m,q,len,ans;
int a[maxl],du[maxl],up[maxl];
vector<int> e[maxl],bige[maxl];
vector<int> num[maxl],b[maxl];
unordered_map<int,int> mp[maxl];
inline void add(int u,int i,int x)
{
i++;
while(i<=du[u]+1)
{
b[u][i]+=x;
i+=i&-i;
}
}
inline void prework()
{
scanf("%d%d",&n,&m);
len=350;
for(register int i=1;i<=n;++i)
{
e[i].clear();bige[i].clear();
mp[i].clear();du[i]=0;
scanf("%d",&a[i]);
}
int u,v;
for(register int i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++)
{
sort(e[i].begin(),e[i].end());
e[i].erase(unique(e[i].begin(),e[i].end()),e[i].end());
du[i]=e[i].size();
}
for(int u=1;u<=n;u++)
{
num[u].resize(du[u]+1);
for(int i=0;i<=du[u];i++)
num[u][i]=0;
b[u].resize(du[u]+2);
for(int i=0;i<=du[u]+1;i++)
b[u][i]=0;
up[u]=log2(du[u]+1);
}
for(int u=1;u<=n;u++)
for(int v:e[u])
if(du[v]>len)
{
bige[u].push_back(v);
if(a[u]<=du[v])
{
if(++num[v][a[u]]==1)
add(v,a[u],1);
}
mp[v][u]=a[u];
}
}
inline int qry(int u)
{
int ans=0,cnt=0;
for(int i=up[u];i>=0;i--)
{
ans+=1<<i;
if(ans>du[u]+1 || cnt+b[u][ans]<ans)
ans-=1<<i;
else
cnt+=b[u][ans];
}
return ans+1-1;
}
inline void mainwork()
{
int op,u,x,v;
scanf("%d",&q);
for(register int t=1;t<=q;++t)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&u,&x);
if(du[u]<=len)
{
for(int v:bige[u])
{
if(a[u]<=du[v])
{
if(--num[v][a[u]]==0)
add(v,a[u],-1);
}
if(x<=du[v])
{
if(++num[v][x]==1)
add(v,x,1);
}
}
}
a[u]=x;
}
else
{
scanf("%d",&u);
if(du[u]<=len)
{
for(int i=0;i<=du[u];i++)
num[u][i]=0;
for(int v:e[u])
if(a[v]<=du[u])
++num[u][a[v]];
for(int i=0;i<=du[u];i++)
if(!num[u][i])
{
ans=i;
break;
}
}
else
{
for(int v:bige[u])
{
if(mp[u][v]==a[v])
continue;
if(mp[u][v]<=du[u])
{
if(--num[u][mp[u][v]]==0)
add(u,mp[u][v],-1);
}
if(a[v]<=du[u])
{
if(++num[u][a[v]]==1)
add(u,a[v],1);
}
mp[u][v]=a[v];
}
ans=qry(u);
}
printf("%d\n",ans);
}
}
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
}
return 0;
}