题意:bc round 74
分析:
参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于k的所有点, 每次找出编号最小的, 并相应的减少k即可.
这个数据结构可以用线段树, 建立一个线段树每个节点[l,r]维护编号从ll到rr的所有节点的最小入度, 查询的时候只需要在线段树上二分,
找到最小的x满足入度小于等于k.
复杂度O((n+m)logn)
#include <iostream> #include <cstdio> #include <vector> #include <cstring> #include <queue> #include <cmath> using namespace std; typedef long long LL; const int mod=1e9+7; const int INF=0x3f3f3f3f; const int N=1e5+5; int d[N],o[N<<2],head[N],p; struct Edge { int v,next; }edge[N*2]; void add(int u,int v) { edge[p].v=v; edge[p].next=head[u]; head[u]=p++; } void pushup(int rt) { o[rt]=min(o[rt*2],o[rt*2+1]); } void build(int rt,int l,int r) { if(l==r) { o[rt]=d[l]; return; } int mid=(l+r)>>1; build(rt*2,l,mid); build(rt*2+1,mid+1,r); pushup(rt); } void update(int rt,int l,int r,int pos) { if(l==r) { o[rt]=d[l]; return; } int mid=(l+r)>>1; if(pos<=mid)update(rt*2,l,mid,pos); else update(rt*2+1,mid+1,r,pos); pushup(rt); } int query(int rt,int l,int r,int c) { if(l==r) return l; int mid=(l+r)>>1; if(o[rt*2]<=c)return query(rt*2,l,mid,c); else return query(rt*2+1,mid+1,r,c); } int main() { int T,n,m,k; scanf("%d", &T); while(T--) { scanf("%d%d%d",&n,&m,&k); memset(head,-1,sizeof(head)); memset(d,0,sizeof(d)); p=0; for(int i=0;i<m;++i) { int u,v; scanf("%d%d",&u,&v); add(u,v); ++d[v]; } build(1,1,n); LL ans=0; for(int i=1;i<=n;++i) { LL x=query(1,1,n,k),y=i; ans=(ans+x*y%mod)%mod; k-=d[x]; d[x]=INF; update(1,1,n,x); for(int j=head[x];~j;j=edge[j].next) { int v=edge[j].v; if(d[v]==INF)continue; --d[v]; update(1,1,n,v); } } printf("%I64d\n",ans); } return 0; }
时间: 2024-12-10 13:11:10