【BZOJ4383】[POI2015]Pustynia 线段树优化建图
【BZOJ4383】[POI2015]Pustynia
Description
给定一个长度为n的正整数序列a,每个数都在1到10^9范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数l,r,k以及接下来k个正整数,表示a[l],a[l+1],...,a[r-1],a[r]里这k个数中的任意一个都比任意一个剩下的r-l+1-k个数大(严格大于,即没有等号)。
请任意构造出一组满足条件的方案,或者判断无解。
Input
第一行包含三个正整数n,s,m(1<=s<=n<=100000,1<=m<=200000)。
接下来s行,每行包含两个正整数p[i],d[i](1<=p[i]<=n,1<=d[i]<=10^9),表示已知a[p[i]]=d[i],保证p[i]递增。
接下来m行,每行一开始为三个正整数l[i],r[i],k[i](1<=l[i]<r[i]<=n,1<=k[i]<=r[i]-l[i]),接下来k[i]个正整数x[1],x[2],...,x[k[i]](l[i]<=x[1]<x[2]<...<x[k[i]]<=r[i]),表示这k[i]个数中的任意一个都比任意一个剩下的r[i]-l[i]+1-k[i]个数大。Σk <= 300,000
Output
若无解,则输出NIE。
否则第一行输出TAK,第二行输出n个正整数,依次输出序列a中每个数。
Sample Input
2 7
5 3
1 4 2 2 3
4 5 1 4
Sample Output
6 7 1000000000 6 3
题解:这种类型的题还真是熟能生巧啊~
我们令一条边权为1的有向边(a,b)表示Va<Vb,边权为0的有向边表示Va<=Vb。然后对于题中给出的限制条件:[l,r]中的{a1,a2,..ak}比其他数都大,我们可以从一个新建的节点u向a1,a2,...ak连边,从剩余的节点向u连边。但是剩余的节点可能很多,我们可以将它们视为k+1个区间,用线段树优化建图。
连完边后跑一边拓扑排序就知道有没有环了,在拓扑排序的同时顺便就能求出可行方案了。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #define lson (x<<1) #define rson (x<<1|1) using namespace std; const int maxn=1000010; int n,N,m,S,cnt,now; int to[3000000],next[3000000],val[3000000],head[maxn],v[maxn],s[maxn],p[maxn],d[maxn]; queue<int> q; void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; //printf("*%d %d %d\n",a,b,c); } void build(int l,int r,int x) { if(l==r) { now=max(now,x+n),v[x+n]=v[l],add(l,x+n,0); return ; } int mid=l+r>>1; add(lson+n,x+n,0),add(rson+n,x+n,0); build(l,mid,lson),build(mid+1,r,rson); } void updata(int l,int r,int x,int a,int b) { if(a>b) return ; if(a<=l&&r<=b) { add(x+n,now,0); return ; } int mid=l+r>>1; if(a<=mid) updata(l,mid,lson,a,b); if(b>mid) updata(mid+1,r,rson,a,b); } int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { //freopen("bz4383.in","r",stdin); //freopen("bz4383.out","w",stdout); n=rd(),S=rd(),m=rd(); int i,j,u,a,b,c; memset(head,-1,sizeof(head)); for(i=1;i<=S;i++) a=rd(),b=rd(),v[a]=b; build(1,n,1); for(i=1;i<=m;i++) { now++,a=rd(),b=rd(),c=rd(); p[0]=a-1,p[c+1]=b+1; for(j=1;j<=c;j++) p[j]=rd(),add(now,p[j],1); for(j=1;j<=c+1;j++) updata(1,n,1,p[j-1]+1,p[j]-1); } for(i=1;i<=now;i++) for(j=head[i];j!=-1;j=next[j]) d[to[j]]++; for(i=1;i<=now;i++) if(!d[i]) q.push(i); while(!q.empty()) { u=q.front(),q.pop(); if(v[u]) { if(s[u]<=v[u]) s[u]=v[u]; else { printf("NIE"); return 0; } } else if(u<=n) s[u]=max(s[u],1); if(s[u]>1000000000) { printf("NIE"); return 0; } for(i=head[u];i!=-1;i=next[i]) { s[to[i]]=max(s[to[i]],s[u]+val[i]),d[to[i]]--; if(!d[to[i]]) q.push(to[i]); } } for(i=1;i<=now;i++) if(d[i]) { printf("NIE"); return 0; } printf("TAK\n"); for(i=1;i<n;i++) printf("%d ",s[i]); printf("%d",s[n]); return 0; }
| 欢迎来原网站坐坐! >原文链接<