P3588 [POI2015] PUS
~~~~~ P3588 [POI2015] PUS ~~~~~ 总题单链接
思路
~~~~~ 这道题的关键点在于线段树优化建图。
~~~~~ 对每条限制新建一个虚电 p p p,将输入的 x 1 ∼ k x_{1\sim k} x1∼k 连向 p p p,再将 p p p 连向区间内单的其他点,建完图后拓扑排序即可。
~~~~~ 但如果每个区间都是 [ 1 , 100000 ] [1,100000] [1,100000] 且 k = 1 k=1 k=1,那么就会连 ∑ k × n \sum k\times n ∑k×n 条边。
~~~~~ 怎么办,发挥想象力,用线段树优化建图。
~~~~~ 每个限制的区间最多会被拆分成 k + 1 k+1 k+1 个区间,暴力枚举区间,用线段树建图即可。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;vector<ll>eg[500005];ll n,s,m,tot,usr;
ll vis[500005],din[500005],mx[500005];class{
public:struct Point{ll L,R,id;}po[400005];void build(ll x,ll y,ll p=1){po[p]={x,y,++tot};if(x==y){eg[po[p].id].push_back(x),din[x]++;return;}ll mid=(x+y)>>1;build(x,mid,p<<1);build(mid+1,y,p<<1|1);eg[po[p].id].push_back(po[p<<1].id),din[po[p<<1].id]++;eg[po[p].id].push_back(po[p<<1|1].id),din[po[p<<1|1].id]++;}void query(ll wc,ll x,ll y,ll p=1){if(po[p].R<x||po[p].L>y)return;if(po[p].L>=x&&po[p].R<=y){eg[wc].push_back(po[p].id);din[po[p].id]++;return;}query(wc,x,y,p<<1);query(wc,x,y,p<<1|1);}
}tr;queue<ll>que;
void tupo(){for(ll i=1;i<=tot;i++)if(!din[i])que.push(i);while(!que.empty()){ll u=que.front();que.pop();usr++;if(mx[u]<1||vis[u]>mx[u]){cout<<"NIE";exit(0);}for(ll v:eg[u]){din[v]--;if(u<=n)mx[v]=min(mx[v],mx[u]-1);else mx[v]=min(mx[v],mx[u]);if(din[v]==0)que.push(v);}}if(usr!=tot){cout<<"NIE";exit(0);}
}vector<ll>now;
signed main(){ios::sync_with_stdio(false);cin>>n>>s>>m;tot=n;for(ll i=1;i<=s;i++){ll x,y;cin>>x>>y;vis[x]=y;}tr.build(1,n);for(ll q=1;q<=m;q++){ll x,y,k;cin>>x>>y>>k;now.clear();for(ll i=1;i<=k;i++){ll g;cin>>g;now.push_back(g);}tot++;if(now[0]-1>=x)tr.query(tot,x,now[0]-1);for(ll i=1;i<now.size();i++){if(now[i-1]+1>now[i]-1)continue;tr.query(tot,now[i-1]+1,now[i]-1);}for(ll it:now)eg[it].push_back(tot),din[tot]++;if(y>=now[now.size()-1]+1)tr.query(tot,now[now.size()-1]+1,y);}for(ll i=1;i<=tot;i++){if(vis[i])mx[i]=vis[i];else mx[i]=1000000000;}tupo();cout<<"TAK"<<endl;for(ll i=1;i<=n;i++)cout<<mx[i]<<" ";return 0;
}