题解 P3588【[POI2015] PUS】
题意
给一个长度为 $n$ 的序列 $a$ 的 $s$ 项,给出 $m$ 条限制,一条限制以 $l,r,k,x_1,x_2,\cdot\cdot\cdot,x_k$ 描述,表示在 $[l,r]$ 区间中,$a_{x_1},a_{x_2},\cdot\cdot\cdot,a_{x_k}$ 都严格大于没被选中的数。
求一种构造方案。
$1\le s\le n\le10^5,1\le m\le 2\times 10^5,\sum k\le 3\times 10^5$
思路
模拟赛 $\text C$ 题,赛时做出来了!!1
首先看到大于,可以想到差分约束,但是要是直接暴力连边的话边数是 $O(\sum k(r-l+1-k))=O(n^2m)$ 的。显然不能通过。
稍加思考可以想到建立两个虚点,两边分别向虚点连边(或被虚点连向),边数是 $O(\sum k+\sum r-l+1-k)=O(nm)$ 的。仍然不能通过。
考虑到第二部分为整个区间被 $k$ 个位置隔开的若干个子区间,直接由子区间向虚点连边,考虑线段树优化建图,边数为 $O(\sum k+\sum \log (r-l+1-k))=O(\sum k+m\log n)$ 的,此时边数已在合理范围内。
接下来考虑解决原问题。考虑拓扑排序,可以同时求出是否有环和最长路,最开始需要先把给出的 $s$ 项的最长路设定为给出的,其余设定为 $1$,此时便可以进行拓扑排序了。
时间复杂度 $O((\sum k+m)\log n)$。
注意,经我测试,极限数据下边数不少于 $5.5\times 10^6$,点数不少于 $6\times 10^5$,许多题解也因此被我 hack 了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=6e5+10,E=6e6+10;
int n,m,s,r[N],ind[N];
int cnt,head[N];
struct Edge{
int to,nxt,w;
}a[E];
inline void add(int u,int v,int w){
cnt++;
a[cnt].w=w;
a[cnt].to=v;
a[cnt].nxt=head[u];
head[u]=cnt;
ind[v]++;
}
int id;
int num[N];
#define ls (rt<<1)
#define rs (rt<<1|1)
inline void build(int rt,int l,int r){
if(l==r){
num[rt]=l;
return ;
}
num[rt]=++id;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
add(num[ls],num[rt],0);
add(num[rs],num[rt],0);
}
inline void add(int rt,int l,int r,int L,int R,int u){
if(L<=l&&r<=R){
add(num[rt],u,0);
return ;
}
int mid=l+r>>1;
if(L<=mid) add(ls,l,mid,L,R,u);
if(R> mid) add(rs,mid+1,r,L,R,u);
}
int dis[N];
bool vis[N];
inline int topo(){
queue<int>q;
for(int i=1;i<=id;i++){
dis[i]=max(dis[i],1);
if(!ind[i]) q.push(i);
}
while(!q.empty()){
int cur=q.front();
q.pop();
vis[cur]=1;
for(int i=head[cur];i;i=a[i].nxt){
int t=a[i].to;
dis[t]=max(dis[t],dis[cur]+a[i].w);
if(r[t]&&dis[t]>r[t]) return -1;
if(--ind[t]==0)
q.push(t);
}
}
for(int i=1;i<=id;i++)
if(!vis[i]||dis[i]>1e9)
return -1;
return 1;
}
int main(){
// freopen("data_C2.in","r",stdin);
// freopen("data_C2.out","w",stdout);
n=id=read(),s=read(),m=read();
build(1,1,n);
for(int i=1;i<=s;i++){
int p=read();
r[p]=dis[p]=read();
}
while(m--){
int l=read(),r=read(),k=read();
id++;
int cur=l-1;
for(int i=1;i<=k;i++){
int p=read();
add(id,p,1);
if(p-1!=cur)
add(1,1,n,cur+1,p-1,id);
if(i==k&&p<r)
add(1,1,n,p+1,r,id);
cur=p;
}
}
if(~topo()){
// printf("%d %d\n",cnt,id);
puts("TAK");
for(int i=1;i<=n;i++)
printf("%d ",dis[i]);
}else{
puts("NIE");
}
}
再见 qwq~

浙公网安备 33010602011771号