[BZOJ 2799]POI 2012 Salaries

25 篇文章 0 订阅

算出每个点取值的范围:1~maxx_i。把没用的点从小到大放在一个数组里,按照maxx_i顺序扫描。

如果当前maxx_i=k的仅有一个点,且在1~k中只有一个数可用,那么这个节点的值确定了。

如果maxx_i=k的节点数==在1~k中可用的数的数量,很明显之前的节点会把这些数占满,1~k中可用的数全置为不可用。

复杂度:O(n)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int Maxn=1000005;
int node[Maxn],next[Maxn],a[Maxn],dig[Maxn];
int z[Maxn],q[Maxn],sum[Maxn],p[Maxn],fa[Maxn];
int n,i,j,tot,root,N,head,l,r,t,maxx[Maxn],id[Maxn];

int read(){
  char ch=getchar();
  int ret=0;
  while (ch<'0' || ch>'9') ch=getchar();
  while (ch>='0' && ch<='9')
    {ret=ret*10+ch-'0'; ch=getchar();}
  return ret;
}

void add(int x,int y)
  { node[++tot]=y; next[tot]=a[x]; a[x]=tot; }

int gf(int x){
  int xx=x, xxx;
  while (xx!=fa[xx]) xx=fa[xx];
  while (x!=xx) xxx=x, x=fa[x], fa[xxx]=xx;
  return xx;
}

void bfs(){
  for (q[l=r=1]=root;l<=r;l++)
  	for (i=a[q[l]];i;i=next[i])
  	 q[++r]=node[i];
  for (i=1;i<=n;i++){
  	if (maxx[ q[i] ]>0) continue;
  	//t = maxx[ q[i] ] =lower_bound( dig+1,dig+N+1,maxx[ p[q[i]] ]-1 )-dig;
  	t = maxx[ q[i] ] = gf( maxx[ p[q[i]] ]-1 );
  	//go[i]=tb[t]; tb[t]=i;
  	sum[t]++; if (sum[t]==1) id[t]=q[i];
  }
}

int main(){
  freopen("pen.in","r",stdin);
  freopen("pen.out","w",stdout);
  //scanf("%d",&n);
  n=read();
  for (i=1;i<=n;i++) fa[i]=i;
  for (i=1;i<=n;i++){
  	//scanf("%d%d",&p[i],&z[i]);
  	p[i]=read(); z[i]=read();
  	if (i!=p[i]) add(p[i],i);
  	  else root=i;
  	if (z[i]>0) maxx[i]=z[i], fa[z[i]]=z[i]-1;
  }
  z[root]=n; maxx[root]=n; fa[n]=n-1;
  for (i=1;i<=n;i++)
    if (fa[i]==i) dig[++N]=i;
  bfs();
  //for (i=1;i<=n;i++) printf("%d\n",maxx[i]);
  //printf("\n");
  for (i=1,j=head=1;i<=n&&head<=N;i++){
  	//printf("%d\n",j);
  	for (;j<=N&&dig[j]<=i;j++);
  	if (sum[i]==1 && j-head==1)
  	  z[ id[i] ]=dig[j-1];
  	if (sum[i-1]+sum[i]==j-head)
	    head=j, sum[i]=0;
	  else sum[i]+=sum[i-1];
  }
  for (i=1;i<=n;i++) printf("%d\n",z[i]);
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值