首先,有一个最简单的思路,把这个区间里的其它数向这
考虑为什么要求最长路。这里的最长路表示该点的最小取值。如果我们求出来的这个点的
但这道题的边数有
考虑第一个优化,我们可以建一个虚拟点
但边的条数我们还是不能接受。
考虑进一步的优化。我们有
最后说一下如何判环,我们发现在拓扑排序中,把每一个点
- #include
- #include
- #include
- #include
- #include
- #include
- #define re register
- #define rep(a,b,c) for(re int a(b) ; a<=c ; ++a)
- #define drep(a,b,c) for(re int a(b) ; a>=c ; --a)
- using namespace std;
- inline int read(){
- int x=0,f=1;char ch=getchar();
- while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
- while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
- return x*f;
- }
- const int M = 5e5+10;
- const int limit = 1e9;
- int a[M],dis[M],head[M],vis[M];
- int du[M],t[M];
- int n,s,m;
- int cnt,tot;
- struct edge{
- int to,nxt,w;
- }e[M*10]; //注意不要开小
- inline void add(int u,int v,int w){ //加边
- e[++cnt].to = v;
- e[cnt].w = w;
- e[cnt].nxt = head[u];
- head[u] = cnt;
- du[v]++;
- }
- inline void build(int k,int l,int r){ //正常的线段树建立
- if(l == r){
- t[k] = l; //叶节点的编号
- return;
- }
- t[k] = ++tot; //每一个节点的编号
- int mid = (l+r)>>1;
- build(k<<1,l,mid);
- build(k<<1|1,mid+1,r);
- add(t[k<<1],t[k],0),add(t[k<<1|1],t[k],0); //左右儿子分别向父亲连边
- }
- inline void link(int k,int l,int r,int x,int y,int node){
- if(x<=l && r<=y){
- add(t[k],node,0); // 这个区间向虚拟点node连边
- return;
- }
- int mid = (l+r)>>1;
- if(x<=mid) link(k<<1,l,mid,x,y,node);
- if(y>mid) link(k<<1|1,mid+1,r,x,y,node); //正常操作
- }
- inline void topo(){
- queue<int> q;
- for(re int i(1) ; i<=tot ; ++i){
- if(!dis[i]) dis[i] = 1; //从1开始
- if(du[i] == 0) q.push(i); //把入度为0的点先加进队列
- }
- while(!q.empty()){
- int u = q.front();
- q.pop();
- vis[u] = 1; //标记这个点走到了
- for(re int i(head[u]) ; i ; i=e[i].nxt){
- int v = e[i].to,w = e[i].w;
- if(dis[v] < dis[u] + w) dis[v] = dis[u] + w; //更新最长路
- if(a[v] && dis[v] > a[v]) printf("NIE\n"),exit(0); //如果不满足条件
- du[v]--;
- if(!du[v]) q.push(v);
- }
- }
- }
- signed main(){
- n=read(),s=read(),m=read();
- tot = n;
- build(1,1,n);
- rep(i,1,s){
- int pos=read(),x=read();
- a[pos] = dis[pos] = x;
- }
- rep(i,1,m){
- int l=read(),r=read(),k=read();
- tot++; //tot就是虚拟点
- int pre = l-1; //上一段区间的左端点
- int x;
- for(re int j(1) ; j<=k ; ++j){
- x = read();
- add(tot,x,1); //虚拟点向x连边
- if(x > pre+1) link(1,1,n,pre+1,x-1,tot); //这个区间没有被连边,就连边
- pre = x; //更新pre
- }
- if(x < r) link(1,1,n,x+1,r,tot); //最后看是否会剩一段区间没有连边
- }
- topo();
- for(re int i(1) ; i<=tot ; ++i) if(!vis[i] || dis[i] > limit) { printf("NIE\n"); return 0; } //判无解
- printf("TAK\n");
- rep(i,1,n) printf("%d ",dis[i]);
- return 0;
- }