BZOJ4383: [POI2015]Pustynia

来源:互联网 发布:openwrt 挂载网络共享 编辑:程序博客网 时间:2024/05/02 04:39

题目大意:给出一些限制条件,每个限制条件都是一个区间内k个数比另外一些都大,问合不合法

题目落下个条件   ∑k≤30W


首先把这些限制条件拆开,每个不连续的区间拆成O(K)个连续的区间,然后就变成了KlogK个限制条件,每个条件是一个数比一段区间里的数都大

这个可以连了边之后用记忆化搜索拓扑序Dp就好了

但是连边是O(N^2)级别的,怎么办呢?

就可以用线段树优化建图啦!

首先对于所有的数建一颗线段树,然后一个点对一个区间连边就相当于对线段树上这个区间连边,这是O(logN)级别的

所以总边数就是O(KlogN)级别的,就可以过了


注意这题有坑点,“每个数都在1到10^9范围内”,这句话是有用的!!!!!!!

#include<iostream>#include<cstdio>#define N 600010#define M 2000010using namespace std;int a[N],l[N],r[N],ch[N][2],cn;int sit[N];int to[M],w[M],nxt[M],pre[N],cnt;int du[N];bool done[N];void ae(int ff,int tt,int ww){cnt++;du[tt]++;to[cnt]=tt;nxt[cnt]=pre[ff];pre[ff]=cnt;w[cnt]=ww;}int build(int ll,int rr){cn++;int x=cn;l[cn]=ll;r[cn]=rr;if(ll==rr){sit[ll]=cn;return x;}int mid=(ll+rr)>>1;ch[x][0]=build(ll,mid);ch[x][1]=build(mid+1,rr);ae(x,ch[x][0],0);ae(x,ch[x][1],0);return x;}void add(int x,int now,int L,int R){if(l[now]==L&&r[now]==R){ae(x,now,0);return;}int mid=(l[now]+r[now])>>1;if(R<=mid) add(x,ch[now][0],L,R);else if(L>mid) add(x,ch[now][1],L,R);else add(x,ch[now][0],L,mid),add(x,ch[now][1],mid+1,R);}void dfs(int x){done[x]=true;int i,j;for(i=pre[x];i;i=nxt[i]){j=to[i];a[j]=min(a[j],a[x]-w[i]);du[j]--;if(!du[j]) dfs(j);}}int c[N],d[N];int main(){int n,s,m;scanf("%d%d%d",&n,&s,&m);int i,j,k,x,y;build(1,n);int tmp=cn;for(i=1;i<=cn;i++) a[i]=1e9;for(i=1;i<=s;i++){scanf("%d%d",&c[i],&d[i]);a[sit[c[i]]]=d[i];}int L,R;while(m--){scanf("%d%d%d",&L,&R,&k);y=L;cn++;for(i=1;i<=k;i++){scanf("%d",&x);ae(sit[x],cn,1);if(x>y) add(cn,1,y,x-1);y=x+1;}if(R>=y) add(cn,1,y,R);}for(i=tmp+1;i<=cn;i++)a[i]=1e9;dfs(1);for(i=1;i<=n;i++)if(!done[sit[i]]||a[sit[i]]<1){puts("NIE");return 0;}for(i=1;i<=s;i++)if(a[sit[c[i]]]!=d[i]){puts("NIE");return 0;}puts("TAK");for(i=1;i<=n;i++)printf("%d ",a[sit[i]]);}

0 0