题目描述
传送门
题目大意:给定一个长度为n的正整数序列a,每个数都在1到
109
范围内,告诉你其中s个数,并给出m条信息,每条信息包含三个数
l,r,k
以及接下来k个正整数,表示
a[l],a[l+1],...,a[r−1],a[r]
里这k个数中的任意一个都比任意一个剩下的
r−l+1−k
个数大(严格大于,即没有等号)。
请任意构造出一组满足条件的方案,或者判断无解。
题解
想办法将题目中给出的大小关系转化成一张图。
对于每条信息,我们新建一个结点now.对于[l,r]中所有给出的较大的位置x[i],now->x[i]边权为0。
所有[l,r]中除x以外的位置y[i],y[i]->now边权为1。这样我们就能表示出x[i]与所有y[i]的关系,至少大1。
然后按照拓扑序求解f[i],表示对结点i限制最紧的答案,及所有能到达i的路径最大权值和。
如果直接暴力建图的话,边太多了,所有利用线段树优化建图,x[i]把区间[l,r]分成了好几段,每次连边的时候直接让线段树中对应的区间连向now。
因为很多点已经给出了权值,那么f[i]应该对a[i]取max,如果f[x]>a[x],那么无解。
如果有某个点的权值超过
109
,那么也是无解。
如果图中存在环,同样也是无解。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#define N 400003
using namespace std;
int ls[N*10],rs[N*10],sz,tot,point[N*10],nxt[N*10],v[N*10];
int n,m,s,c[N*10],pos[N],f[N],a[N],du[N],root,mark[N];
void add(int x,int y,int k)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=k; du[y]++;
}
void build(int &i,int l,int r)
{
i=++sz;
if (l==r) {
pos[l]=i;
return;
}
int mid=(l+r)/2;
build(ls[i],l,mid); build(rs[i],mid+1,r);
add(ls[i],i,0); add(rs[i],i,0);
}
void query(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) {
add(now,sz,1);
return;
}
int mid=(l+r)/2;
if (ll<=mid) query(ls[now],l,mid,ll,rr);
if (rr>mid) query(rs[now],mid+1,r,ll,rr);
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&s,&m);
build(root,1,n);
for (int i=1;i<=s;i++){
int x,v; scanf("%d%d",&x,&v);
a[pos[x]]=v;
}
for (int i=1;i<=m;i++){
++sz;
int l,r,k; scanf("%d%d%d",&l,&r,&k);
int L=l-1;
for (int j=1;j<=k;j++) {
int x; scanf("%d",&x);
add(sz,pos[x],0);
if(L+1<x) query(root,1,n,L+1,x-1);
L=x;
}
if (L<r) query(root,1,n,L+1,r);
}
queue<int> p;
for (int i=1;i<=sz;i++)
if (!du[i]) {
f[i]=1; p.push(i);
mark[i]=1;
}
while (!p.empty()){
int x=p.front(); p.pop();
if (f[x]>a[x]&&a[x]) {
printf("NIE\n");
return 0;
}
else f[x]=max(a[x],f[x]);
if (f[x]>1000000000) {
printf("NIE\n");
return 0;
}
for (int i=point[x];i;i=nxt[i]){
f[v[i]]=max(f[v[i]],f[x]+c[i]);
du[v[i]]--;
if(!du[v[i]]) {
p.push(v[i]);
mark[v[i]]=1;
}
}
}
int cnt=0;
for (int i=1;i<=sz;i++) cnt+=mark[i];
if (cnt!=sz) {
printf("NIE\n");
return 0;
}
printf("TAK\n");
for (int i=1;i<=n;i++) printf("%d ",f[pos[i]]);
printf("\n");
}