适用题目特征 一个点/区间向一个区间上的所有点建边。 原理 对每个需要建边的区间上的所有点建立一棵线段树,通过向线段树建边,取代向线段树上所有叶子节点建边的操作。 例题 Luogu P3588 代码如下 /* Luogu P3588 */ #define method_1 #ifdef method_1 /* */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define D(x) cout<<#x<<" = "<=(s);i--) using namespace std; typedef long long ll; typedef pairpii; const int maxn=5e5+5; const int INF=0x3f3f3f3f; int n,s,m; int id[maxn*(4+1)],cnt; struct segmentTree{ int l,r; }tr[maxn*(4+1)]; int head[maxn*(4+1)],tot=1; int in[maxn]; struct node{ int from,to,w; }edge[(maxn*(4+1))<<1]; void add(int from,int to,int w){ edge[++tot].from=head[from],edge[tot].to=to,edge[tot].w=w,head[from]=tot,in[to]++; } void build(int rt,int l,int r){ tr[rt].l=l,tr[rt].r=r; if(l==r) {id[rt]=l;return;} id[rt]=++cnt; int mid=l+r>>1; build(rt<<1,l,mid),build(rt<<1|1,mid+1,r); add(id[rt<<1],id[rt],0),add(id[rt<<1|1],id[rt],0); } void update(int rt,int l,int r,int x){ if(l<=tr[rt].l&&tr[rt].r<=r){add(id[rt],x,0);return;} int mid=tr[rt].l+tr[rt].r>>1; if(l<=mid) update(rt<<1,l,r,x); if(r>mid) update(rt<<1|1,l,r,x); } queueq; int a[maxn*(4+1)],dis[maxn*(4+1)]; int v[maxn*(4+1)]; void toposort(){ rep(i,1,cnt) {dis[i]=dis[i]?dis[i]:1;if(!in[i]) q.push(i);} while(q.size()){ int x=q.front();q.pop();v[x]=1; for(int i=head[x];i;i=edge[i].from){ int y=edge[i].to,w=edge[i].w; dis[y]=max(dis[x]+w,dis[y]); if(a[y]&&dis[y]>a[y]){printf("NIE");exit(0);} if(--in[y]==0) q.push(y); } } } void solve(){ int l,r,k; rep(i,1,m){ scanf("%d%d%d",&l,&r,&k); int pre=l-1;cnt++; int x;while(k--){ scanf("%d",&x); if(x>pre+1) update(1,pre+1,x-1,cnt); add(cnt,x,1);pre=x; } if(pre1e9){printf("NIE");return;} printf("TAK\n"); rep(i,1,n) printf("%d ",dis[i]); } int main() { // ios::sync_with_stdio(false); // freopen("PUS.in","r",stdin); scanf("%d%d%d",&n,&s,&m);cnt=n; build(1,1,n); int p,d; rep(i,1,s) scanf("%d%d",&p,&d),a[p]=dis[p]=d; solve(); return 0; } #endif #ifdef method_2 /* */ #endif #ifdef method_3 /* */ #endif