HDU 6756 Finding a MEX
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 2512 Accepted Submission(s): 492
Problem Description
Given an undirected graph G=(V,E). All vertices are numbered from 1 to N. And every vertex u has a value of Au. Let Su={Av│(u,v)∈E}. Also, F(u) equals MEX(minimum excludant) value of Su. A MEX value of a set is the smallest non-negative integer which doesn’t exist in the set.
There are two types of queries.
Type 1: 1 u x – Change Au to x (0≤x≤109).
Type 2: 2 u – Calculate the value of F(u).
For each query of type 2, you should answer the query.
Input
The first line of input contains a single integer T (1≤T≤10) denoting the number of test cases. Each test case begins with a single line containing two integers n (1≤n≤105), m (1≤m≤105) denoting the number of vertices and number of edges in the given graph.
The next line contains n integers and ith of them is a value of Ai (0≤Ai≤109).
The next m lines contain edges of the graph. Every line contains two integers u, v meaning there exist an edge between vertex u and v.
The next line contains a single integer q (1≤q≤105) denoting the number of queries.
The next q lines contain queries described in the description.
Output
For each query of type 2, output the value of F(u) in a single line.
Sample Input
1
5 4
0 1 2 0 1
1 2
1 3
2 4
2 5
5
2 2
1 2 2
2 2
1 3 1
2 1
Sample Output
2
2
0
把每个点分成两部分,一个是度数 >= sqrt(N)的点,一个是度数 < sqrt(N)的点,前者称为大堆,后者称为小堆,小堆暴力,大堆用数据结构(树状数组)维护,然后通过二分查询不存在的最小非正整数MEX。查询时二分某个位置,看看前面是不是已经填满。
代码内有解释
Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
#define ll long long
using namespace std;
const int INF=0x3f3f3f3f;
const double pi=acos(-1),eps=1e-8;
const int N=1e5+5;
/*
G[N]代表建立的图;
a[N]每个点的值
d[N]每个点的节点个数
idx[N]堆的编号
vector<int>nb[350],当前点权值为a[i]的个数
vector<int>bt[350],树状数组
vector<int>mx[N]大根的储存
*/
vector<int>bt[350],nb[350];
vector<int>G[N],mx[N];
int sz[350],idx[N],cnt;
int a[N],n,m;
int d[N],limit;
bool vis[350];
int lowbit(int x)
{
return x&(-x);
}
int Ask(int x,int y)//树状数组查询
{
int num=0;
for(int i=y;i>0;i-=lowbit(i))
{
num+=bt[x][i];
}
return num;
}
void Add(int x,int y,int w)//树状数组修改
{
for(int i=y;i<=sz[x];i+=lowbit(i))
{
bt[x][i]+=w;
}
}
void Calc(int x,int w)
{
for(int i=0;i<mx[x].size();i++)//大根修改
{
int id=idx[mx[x][i]];
if(a[x]<=d[mx[x][i]])
{
nb[id][a[x]]--;
if(nb[id][a[x]]==0&&a[x])
{
Add(id,a[x],-1);
}
}
if(w<=d[mx[x][i]])
{
nb[id][w]++;
if(nb[id][w]==1&&w)
{
Add(id,w,1);
}
}
}
a[x]=w;//小根修改
}
int Ask_Big(int x)//大根二分,树状数组上求值
{
if(!nb[idx[x]][0])return 0;//0特判
int l=0,r=sz[idx[x]];
int mex=sz[idx[x]];
while(l<=r)
{
int mid=(l+r)/2;
if(Ask(idx[x],mid)<mid)
{
mex=mid,r=mid-1;
}
else
l=mid+1;
}
return mex;
}
int Ask_Small(int x)//小根暴力
{
for(int i=0;i<=d[x];i++)
{
vis[i]=false;
}
for(int i=0;i<G[x].size();i++)
{
if(a[G[x][i]]<=d[x])
{
vis[a[G[x][i]]]=true;
}
}
for(int i=0;i<=d[x];i++)
{
if(!vis[i])
return i;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=0; i<=n; i++)
{
G[i].clear();
mx[i].clear();
d[i]=0;
}
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
for(int i=0; i<m; i++)
{
int x,y;
scanf("%d %d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
d[x]++;
d[y]++;
}
cnt=0;
limit=sqrt(n);
for(int i=1; i<=n; i++)
{
if(d[i]<limit)
continue;
idx[i]=++cnt;
sz[cnt]=d[i];
bt[cnt].resize(d[i]+5);
nb[cnt].resize(d[i]+5);
for(int j=0; j<=d[i]; j++)
{
bt[cnt][j]=0;
nb[cnt][j]=0;
}
for(int j=0;j<G[i].size();j++)
{
if(a[G[i][j]]>d[i])//超过度数的不加,因为MEX肯定取不到这个值
continue;
nb[cnt][a[G[i][j]]]++;
if(nb[cnt][a[G[i][j]]]==1&&a[G[i][j]])
Add(cnt,a[G[i][j]],1);
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<G[i].size();j++)
{
if(d[G[i][j]]>=limit)
mx[i].push_back(G[i][j]);
}
}
int q;
scanf("%d",&q);
while(q--)
{
int f,x,w;
scanf("%d %d",&f,&x);
if(f==1)
{
scanf("%d",&w);
Calc(x,w);
}else
{
if(d[x]>=limit)
{
printf("%d\n",Ask_Big(x));
}else{
printf("%d\n",Ask_Small(x));
}
}
}
}
return 0;
}