1 条题解
-
0
自动搬运
来自洛谷,原作者为

BJpers2
**搬运于
2025-08-24 21:50:29,当前版本为作者最后更新于2018-07-31 20:25:02,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
我们在做题之前,先要知道这是一道图论题
题意描述有些绕,我在这里再阐述一遍:
- 给出一个部分未知的数列的长,以及数列已知的部分
- 再给出一些区间。对于每一个区间,在它的内部钦定一些位置,并要求这些位置上的数最后的值,都严格大于区间内其他未钦定的位置上的数。
- 要求给出任意一种可行的满足条件的数列。
大于关系可以看做是一条边,由较大的数指向较小的数。对于每一个询问,我们考虑让这k个位置上的数与区间内其他的位置两两连有向边。这样一来,问题就转化到图上了。
首先,图上若有环则一定无解,因为它意味着。
那么问题变为DAG上问题,只要我们从入读为0的位置开始DP即可。贪心的想,最大的数越大,留给下面的空间就越大(注意数列中的数大于0!),所以每个数的可能值全部设置为1e9。DP时假如遇到了已经确定的数,却发现它撑死了也打不到他预设的值(否则与其他单调关系矛盾),那么问题无解。
最后若有解,输出答案即可。
好完美啊,在你看到之前还以为是提高组水题。
假如按之前说的那样建图,一次操作就要加入条边,最坏情况下加入的边数在级别,空间直接爆炸。
也许你会说,我们可以采用“电话交换机”的建图思想,不再两两连边 而是新拉出一个超级节点,然后只需要k个点向它连边,它向区间内其余点连边即可,只要条边即可
然而这还是不行。因为如果每个操作区间都是上十万的大区间,而k每次只有1,最多还是要连条边,仍然爆炸。
注意到,每次我们的空间浪费在,有大量位置连续的点,超级节点却向它们一一连了边。发挥想象力,有什么东西能提高区间操作的效率呢?
线段树!!!
没错,我们把数列建成一棵线段树,然后对于每个操作区间,它会被k个点割成最多k+1个子区间,对于每个区间,可以化成线段树上的最多个已知区间,对于超级节点连出的边,一次操作要加条,算上连向超级节点的,总共是。于是总共的边数在级别,完全可以接受。
这样连边以后,要注意线段树自身的边只是形式,要赋权为0。
之后按照之前所说的,按拓扑序DP就行了。
#include<iostream> #include<cstdio> #include<queue> #define lson ls[u],l,md #define rson rs[u],md+1,r #define FOR(i,a,b) for(int i=a;i<=b;i++) #define REP(u) for(int i=hd[u],v=e[i].v,w=e[i].w;i;i=e[i].n,v=e[i].v,w=e[i].w) using namespace std; const int N=100100,TO=600600,M=7007000,INF=1000000000; struct edge{int n,v,w;}e[M]; int n,s,m,p,v,u,x,k,l,r,pr,cnt,fl,ok,id[N],ins[TO]; int hd[TO],vis[TO],in[TO],f[TO],ls[TO],rs[TO],o[TO]; void add(int u,int v,int w){e[++fl]=(edge){hd[u],v,w};hd[u]=fl;in[v]++;} queue<int>q; int read(){ char ch=getchar();int x=0,o=1; for(;ch<'0' || '9'<ch;ch=getchar()) if(ch=='-') o=-1; for(;'0'<=ch&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^'0'); return x*o; } void prg(){ FOR(x,1,cnt){ cout<<x<<':'; REP(x) cout<<v<<'-'<<w<<' ';cout<<endl; } } void bld(int u,int l,int r){ if(l==r) {id[l]=u;cnt=max(cnt,u);return;} int md=l+r>>1; ls[u]=++cnt,rs[u]=++cnt; bld(lson),bld(rson); add(u,ls[u],0),add(u,rs[u],0); } void adde(int u,int l,int r,int x,int y,int z){ if(x<=l && r<=y) {add(z,u,1);return;} if( y<l || r<x ) return; int md=l+r>>1; adde(lson,x,y,z),adde(rson,x,y,z); } void dfs(int u){ vis[u]=1;ins[u]=1; REP(u){ if(ins[v]) ok=0; if(!vis[v]) dfs(v); } ins[u]=0; } int main(){ scanf("%d%d%d",&n,&s,&m); cnt=1,bld(1,1,n); FOR(i,1,s) p=read(),f[id[p]]=read(),o[id[p]]=f[id[p]]; FOR(i,1,m){ l=read(),r=read(),k=read(); pr=l;++cnt; FOR(j,1,k){ x=read(); add(id[x],cnt,0); if(pr<=x-1) adde(1,1,n,pr,x-1,cnt); pr=x+1; }if(pr<=r) adde(1,1,n,pr,r,cnt); } FOR(i,1,cnt) if(!f[i]) f[i]=INF; FOR(i,1,cnt) if(!vis[i]){ ok=1;dfs(i); if(!ok){printf("NIE");return 0;} } FOR(i,1,cnt) if(!in[i]) q.push(i); while(!q.empty()){ u=q.front();q.pop(); REP(u){ in[v]--; if(o[v]>f[u]-w){printf("NIE");return 0;} f[v]=min(f[v],f[u]-w); if(!in[v]) q.push(v); if(f[v]<1){printf("NIE");return 0;} } } printf("TAK\n"); FOR(i,1,n) printf("%d ",f[id[i]]); }
- 1
信息
- ID
- 2661
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者