题目大意:给出一些限制条件,每个限制条件都是一个区间内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 2000010 using 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]]); }