题目大意:给出一些限制条件,每个限制条件都是一个区间内k个数比另外一些都大,问合不合法
题目落下个条件 ∑k≤30W
首先把这些限制条件拆开,每个不连续的区间拆成O(K)个连续的区间,然后就变成了KlogK个限制条件,每个条件是一个数比一段区间里的数都大
这个可以连了边之后用记忆化搜索拓扑序Dp就好了
但是连边是O(N^2)级别的,怎么办呢?
就可以用线段树优化建图啦!
首先对于所有的数建一颗线段树,然后一个点对一个区间连边就相当于对线段树上这个区间连边,这是O(logN)级别的
所以总边数就是O(KlogN)级别的,就可以过了
注意这题有坑点,“每个数都在1到10^9范围内”,这句话是有用的!!!!!!!
#include
#include
#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]]);
}