题目描述
她们玩得很开心,但是现在到了她们应当离开的时间了,她们会一个接一个地离开。她们想要以某种顺序离开,使得只要至少还有两头奶牛尚未离开,所有尚未离开的奶牛都还有没有离开的朋友。此外,由于行李寄存的因素,有M对奶牛必须满足奶牛要比奶牛先离开。注意奶牛和奶牛可能是朋友,也可能不是朋友。
输入描述:
输入的第行包含两个空格分隔的整数和。输入的第行,每行包含两个整数和,满足,表示奶牛和奶牛是朋友关系。输入的第行,每行包含两个整数和,满足,表示奶牛必须比奶牛先离开聚会。输入保证。对于占总分20%的测试数据,保证。
输出描述:
输出行,每行包含一个整数,如果奶牛可以成为最后一头离开的奶牛,则,否则。
示例1
5 1
1 2
2 3
3 4
4 5
2 4
0
0
1
1
1
解答
给你一棵个节点的树,每次删去一个叶子节点,直到剩下一个节点
但是有个限制,表示如果没被删除,不能删除
最后输出所有能成为最后一个节点的节点
显而易见,对于每个限制,以为根时的子树(包括自己)都是不可行点
于是我们可以写出一个支持子树修改和换根数据结构,比如,树链剖分(参见)
但是单单这个结论是不够的,我们观察一下这组数据
3 2
1 2
2 3
1 3
3 1
0
0
0
0
1
0
因为没有考虑无解情况
那么如何考虑无解情况呢
该题中的无解就是指没有一个点是可行点,那么我们其实只要找出任何一个可行点就可以了
怎么找?
我们将树上的条边连成双向边,将限制里的连向一条单向边
那么,我们每次就只能删除入度为的点
因为如果这个点不是叶子节点,入度至少为,如果有被连有向边(被约束),入度也至少为
随便找一个入度为的点删除,接下来将它连向的边都删除(也就是说连向的点入度--),然后如果更新后连向的点的入度为(不需要每次统计,只统计这次删除更新的点就行了),加入待定删除点
最后,如果还没有删除个点就无法继续删了(没有入度为的点了,比如每个点都被互相约束),说明无解,否则有解
这部分代码(为了方便第二种解法,我用了存(是双向边,是单向边)):
queue<int>q; int findroot() { while(!q.empty()) { int x=q.front(); q.pop(); if(!deg[x]/*入度*/)return x; for(int i=0;i<to[x].size();i++) { if(--deg[to[x][i]]==1) { q.push(to[x][i]); } } for(int i=0;i<d[x].size();i++) { if(--deg[d[x][i]]==1) { q.push(d[x][i]); } } } return 0; }
这样写的话,时间复杂度为
代码:咕咕咕
但是我们刚刚那个方法显然不够优(太暴力了),虽然足以通过本题,但是如果数据加到,显然过不了(吧,有松松松真传我也没办法2333)
首先我们要证(kou)明(hu)两个结论:
一,可行点连通
因为每次修改不可行点只是修改子树,并不破坏可行点的连通性,所以成立
二,任意一个可行点作为根,删除每个限制中的的子树,剩下的都是可行点
这个我真不会证,我的想法是将可行点看为树的中间部分,不可行点是树的一些边角,从中间往四周边角扩散不需要考虑从哪里开始,由于可行点连通,所以不需要每次换根
我们将的位置打上标记,以的返回值(任意一个可行点)为起始点,遇到标记直接,最后输出答案即可
时间复杂度:
总时间复杂度
#include<iostream> #include<vector> #include<queue> using namespace std; vector<int>to[100001],d[100001]; queue<int>q; int n,m,deg[100001]; bool vis[100001],ans[100001]; int findroot() { while(!q.empty()) { int x=q.front(); q.pop(); if(!deg[x])return x; for(int i=0;i<to[x].size();i++) { if(--deg[to[x][i]]==1) { q.push(to[x][i]); } } for(int i=0;i<d[x].size();i++) { if(--deg[d[x][i]]==1) { q.push(d[x][i]); } } } return 0; } void dfs(int x,int f) { ans[x]=1; for(int i=0;i<to[x].size();i++) { if(!vis[to[x][i]]&&f!=to[x][i]) { dfs(to[x][i],x); } } } int main() { cin>>n>>m; for(int i=1;i<n;i++) { int x,y; cin>>x>>y; to[x].push_back(y); to[y].push_back(x); deg[x]++; deg[y]++; } for(int i=1;i<=m;i++) { int x,y; cin>>x>>y; d[x].push_back(y); deg[y]++; vis[x]=1; } for(int i=1;i<=n;i++) { if(deg[i]==1)q.push(i); } int root=findroot(); dfs(root,0); for(int i=1;i<=n;i++) { cout<<ans[i]<<endl; } return 0; }