bzoj 2799 [Poi2012]Salaries 性质+二分
题目大意
给出一棵n个结点的有根树,结点用正整数1~n编号。
每个结点有一个1~n的正整数权值,不同结点的权值不相同,
并且一个结点的权值一定比它父结点的权值小(根结点的权值最大,一定是n)。
现在有些结点的权值是已知的,并且如果一个结点的权值已知,它父结点的权值也一定已知。
问还有哪些结点的权值能够唯一确定。
最后输出每个点的全值,不知道输出0
n<=1,000,000
分析
我们求出每个点最大可以是什么权值
如果i点权值<=d,而<=d中的权值已经确定了d-1个,那么i的权值也可以确定
按照贪心,我们从小到大填权值
做法
求每个点最大权值就dfs+二分
后面选数就按权值从小到大扫就可以了
solution
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
const int M=1000007;
inline int rd(){
int x=0;bool f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(;isdigit(c);c=getchar()) x=x*10+c-48;
return f?x:-x;
}
int n,rt;
int g[M],te;
struct edge{int y,next;}e[M];
int vis[M];
int val[M];
int pre[M];
int a[M],tot;
struct node{
int d,id;
node (int dd=0,int ii=0){d=dd;id=ii;}
}b[M];
int num;
bool cmp(node x,node y){return x.d<y.d;}
void addedge(int x,int y){
e[++te].y=y;e[te].next=g[x];g[x]=te;
}
int find(int d){
int l=1,r=tot,mid;
while(l<r){
int mid=(l+r)/2+1;
if(a[mid]>=d) r=mid-1;
else l=mid;
}
return a[l];
}
void dfs(int x,int mx){
if(val[x]) mx=val[x];
else {
mx=find(mx);
b[++num]=node(mx,x);
}
int p,y;
for(p=g[x];p;p=e[p].next)
if((y=e[p].y)!=pre[x]){
dfs(y,mx);
}
}
int main(){
int i,j,x,y,z;
n=rd();
for(i=1;i<=n;i++){
y=rd(),z=rd();
if(i==y) rt=i;
else pre[i]=y,addedge(y,i);
val[i]=z;
vis[z]=1;
}
for(i=1;i<=n;i++)
if(!vis[i]) a[++tot]=i;
dfs(rt,n);
sort(b+1,b+num+1,cmp);
int cnt=0;
j=1;
for(i=1;i<=n;i++){
if(vis[i]){
cnt++;
continue;
}
if(b[j].d==i){
int tp=j;
for(;j<=num&&b[j].d==i;j++);
if(cnt==i-1) val[b[j-1].id]=i;
cnt+=j-tp;
}
}
for(i=1;i<=n;i++) printf("%d\n",val[i]);
return 0;
}