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

mydiplomacy
**搬运于
2025-08-24 22:07:50,当前版本为作者最后更新于2018-12-20 18:19:58,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
如果A同学观察B同学,那么我们连一条从点A到点B的有向边。
在下面的陈述中,我们将“A同学收到指令”称为“点A被染色”。
那么我们就可以把题意转化为:
给定一个有向图,已知当一个点连向的点被染色时,那么这个点也被染色。支持两种操作:
查询操作:给定区间内的所有点被染色,那么求还需要给多少点染色,才能使最终所有点被染色。
修改操作:给定区间和,将编号在区间内的所有点连向x。
下面考虑做法。
-
20分:
完全暴力即可。对于每个查询操作,首先忽略所有内的点和被染色后被染色的点。接下来枚举每个点是否被选中,对于每种方案进行判定是否合法,最后输出最小答案。
-
50分:
需要进一步研究性质。
首先,由于边只会从编号大的点连到编号小的点,那么这个图一定是一个有向无环图。
观察所有出度为零(指图中不存在这个点连出的边)的点。我们可以证明:
-
如果这些点当中的每个点,如果这个点没被包含,那么这个点必须被染色。
证明:由于这个点没有连出的边,那么如果任何其他点被染色了,这个点都不会被染色。
-
当所有这些点被染色了,那么所有点都会被染色。
证明:假设存在一个点,当所有这些点被染色时,还没有被染色。于是我们可以推出,这个点所连向的所有点都没有被染色。所以,这个点所连向的点所连向的点也没有被染色,以此类推。由于点的数量是有限的,所以必定会出现下列情况之一:
- 这个点所连向的点所连向的点...所连向的点,与这个点重合,或者与另外一个这个点所连向的点...所连向的点重合。这与无环矛盾。
- 这个点所连向的点...所连向的点,没有连出的边。由于我们前面的推导,这个点一定没有染色。而这又与我们假设的前提矛盾。
所以,假设不成立!
于是,所有点被染色的充分必要条件是所有出度为零的点被染色。所以这道题的询问操作就转化为了:查询有多少出度为零的点,不在区间内。只需首先预处理 出入度为零的点,修改操作时移除所有区间内的点,均可以处理。
50分代码见下面参考代码1。
-
-
70分(所有具备特殊性质的数据点):
我们可以首先预处理每个点是否为出度为零的点并记录,接下来预处理出,代表~中有多少个出度为零的点。
对于每个修改操作,只需要将内的每个点标记为出度非零,再根据之前处理出的每个点的是否入度为零标记重新计算数组。效率O(n)
对于每个查询操作,利用前缀和查询与内共有多少个出度非零的点,然后输出。效率
但由于修改不超过100组,可以得到部分分。
70分代码见下面参考代码2。
-
100分:
利用线段树等数据结构维护数组,代表是否出度为零,如果是则为1,否则为0。
查询操作相当于查询的和加上的和。
修改操作相当于将区间区间覆盖为0。
问题得解。
std见下面参考代码3。
参考代码1:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=300005; int d[maxn]; //d代表:当前节点出度是否为零。1:出度为零,0:出度非零 int main() { int n,q; scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) d[i]=1; for(int i=1;i<=n;i++) { int x,y; scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d",&y); d[y]=0; //出度非零 } } for(int i=1;i<=q;i++) { int opt,x,y,z; scanf("%d",&opt); if(opt==1) { int sum=0; scanf("%d %d",&x,&y); for(int j=1;j<x;j++) sum+=d[j]; for(int j=y+1;j<=n;j++) sum+=d[j]; printf("%d\n",sum); } if(opt==2) { scanf("%d %d %d",&x,&y,&z); for(int i=x;i<=y;i++) d[i]=0; } } return 0; }参考代码2:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=300005; int d[maxn],s[maxn]; //s:前缀和 int main() { int n,q; scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) d[i]=1; for(int i=1;i<=n;i++) { int x,y; scanf("%d",&x); for(int j=1;j<=x;j++) { scanf("%d",&y); d[y]=0; } } for(int i=1;i<=n;i++) s[i]=s[i-1]+d[i]; for(int i=1;i<=q;i++) { int opt,x,y,z; scanf("%d",&opt); if(opt==1) { int sum=0; scanf("%d %d",&x,&y); sum=s[x-1]+s[n]-s[y]; printf("%d\n",sum); } if(opt==2) { scanf("%d %d %d",&x,&y,&z); for(int i=x;i<=y;i++) d[i]=0; for(int i=1;i<=n;i++) s[i]=s[i-1]+d[i]; //修改操作后,重新计算前缀和 } } return 0; }参考代码3:
#include <iostream> #include <cstdio> using namespace std; const int maxn=900005; struct Node //线段树节点 { int left, right; int sum, lazy; }a[maxn]; int ab[maxn],x,y,z,opt,d[maxn]; int n,q; void buildtree(int id, int l, int r) { a[id].left=l; a[id].right=r; a[id].lazy=0; if(l==r) { if(d[l]==1) a[id].sum=0; else a[id].sum=1; return; } int mid=(l+r)/2; buildtree(id<<1,l,mid); buildtree(id<<1|1,mid+1,r); a[id].sum=a[id<<1].sum+a[id<<1|1].sum; } void pushdown(int id) { if(a[id].lazy==1) { a[id].sum=0; a[id<<1].lazy=1; a[id<<1|1].lazy=1; a[id].lazy=0; } } int query(int id, int l, int r) { if(a[id].left==l && a[id].right==r) { if(a[id].lazy==0) return a[id].sum; else return 0; } pushdown(id); if(r<=a[id<<1].right) return query(id<<1,l,r); else if(l>=a[id<<1|1].left) return query(id<<1|1,l,r); else { return query(id<<1,l,a[id<<1].right)+query(id<<1|1,a[id<<1|1].left,r); } } void change(int id, int l, int r) { if(a[id].left==l && a[id].right==r) { a[id].lazy=1; return; } pushdown(id); if(r<=a[id<<1].right) change(id<<1,l,r); else if(l>=a[id<<1|1].left) change(id<<1|1,l,r); else { change(id<<1,l,a[id<<1].right); change(id<<1|1,a[id<<1|1].left,r); } a[id].sum=(a[id<<1].lazy==0?a[id<<1].sum:0)+(a[id<<1|1].lazy==0?a[id<<1|1].sum:0); } int main() { scanf("%d %d",&n,&q); for(int i=1;i<=n;i++) { scanf("%d",&ab[i]); for(int j=1;j<=ab[i];j++) { scanf("%d",&x); d[x]=1; } } buildtree(1,1,n); for(int i=1;i<=q;i++) { scanf("%d",&opt); if(opt==1) { scanf("%d %d",&x,&y); printf("%d\n",query(1,1,n)-query(1,x,y)); } else { scanf("%d %d %d",&x,&y,&z); change(1,x,y); } } return 0; } -
- 1
信息
- ID
- 4073
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者