http://hihocoder.com/problemset/problem/1041
每当我以为 dfs 我学得还可以了,就会发现自己其实对某些性质运用的并不是那么熟练
(向黑恶势力低头
其实 这题考点主要是图的dfs ,但是中间的某个步骤可以用bitset 优化一下;
题意:
给一颗树, 每条边 可以走两次,来回各一次。 给你一个遍历顺序,问是否能够按照顺序遍历。
比如:
7
1 2
1 3
2 4
2 5
3 6
3 7
如果按 3 7 2 遍历 可行, 但是按照 3 2 7 遍历则不可以,因为边只能走两次;
然后因为这题 n 比较小,所以可以强行暴力 硬A 过去。
这里讲一个O n 时间复杂度的方法,利用的就是dfs的性质
先说一个方法:
void dfs(int rot,int pre){
vis[rot][rot]=1;
for(int i=head[rot];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
dfs(v,rot);
for(int j=1;j<=n;j++){
vis[rot][j] |=vis[v][j];
}
}
}
看看这个小套路 利用dfs 小性质 来完成 某些点到某些点的子树。
但这里显然是int vis[N][N] 来完成的, 不但可能开不下,而且这里还是n*n 的复杂度,谈何On 呢
这里显然就要用到 bitset,
这里 插入学习一下bitset。
可以当作一个bool型数组考虑,
bitset< N> bs; 可以考虑成一个数组bool bs[N]。
相关操作:
bs.set(); 全部置1,bs.reset()全部置0;
bs.set(pos);等价于bs[pos]=1,bs.reset(pos)等价于bs[pos]=0;
最重点的来了,bitset< N> a, b;
a和b可以直接进行操作,
!a //按位取反
a^b //按位异或
a|b //按位或
a&b //按位与
a=b<<3 //整体移位
a.count(); //a中1的个数
所以上面那段代码
void dfs(int rot,int pre){
vis[rot][rot]=1;
for(int i=head[rot];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
dfs(v,rot);
vis[rot]|=vis[v]; //因为二进制运算 这里的时间复杂度 就是常数级别
}
}
然后我们知道了这个
接下来的处理方式 就仁者见仁了。
我们可以这样
void dfs2(int rot,int pre){
if(flag) return;
if(pos==m){
flag=1;
return;
}
if(rot==f[pos])
pos++; // 走下个点
num[rot]++; // num这点走了一次
while(pos<=m){
int cnt=pos;
for(int i=head[rot];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre || num[v]) continue; //若一条路一个方向只能走一次
if(vis[v][f[pos]]){
dfs2(v,rot);
}
}
if(cnt==pos)
break;
}
}
这个的精髓就在于利用的dfs 的回溯走了那一遍回来的路。
比如 3 7 2
1走到3 之后 又走到7 之后 回溯到1 又走到 2
327 的话 走完2之后 就走不了3 -7 ,因为3走过一遍。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<bitset>
#define mem(a) memset(a,0,sizeof(a))
#define ll __int64
#define INF 0x7fffffff //INT_MAX
#define inf 0x3f3f3f3f //
const double PI = acos(-1.0);
const double e = exp(1.0);
template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; }
using namespace std;
/*
* 学习一下bitset。
可以当作一个bool型数组考虑,
bitset<N> bs; 可以考虑成一个数组bool bs[N]。
相关操作:
bs.set(); 全部置1,bs.reset()全部置0;
bs.set(pos);等价于bs[pos]=1,bs.reset(pos)等价于bs[pos]=0;
最重点的来了,bitset<N> a, b;
a和b可以直接进行操作,
!a //按位取反
a^b //按位异或
a|b //按位或
a&b //按位与
a=b<<3 //整体移位
a.count(); //a中1的个数
*/
//int vis[105][105];
bitset<105>vis[105];
struct Edge{
int to;
int next;
int use;
}edge[1000];
int cnt;
int head[1005];
int n,m;
void addedge(int u,int v){
edge[cnt].to=v;
edge[cnt].use=0;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void dfs(int rot,int pre){
vis[rot][rot]=1;
for(int i=head[rot];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
dfs(v,rot);
vis[rot]|=vis[v];
}
}
int flag;
int pos;
int f[105];
int num[105];
void dfs2(int rot,int pre){
if(flag) return;
if(pos==m){
flag=1;
return;
}
if(rot==f[pos])
pos++; // 走下个点
num[rot]++; // num这点走了一次
while(pos<=m){
int cnt=pos;
for(int i=head[rot];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v==pre || num[v]) continue; //若一条路一个方向只能走一次
if(vis[v][f[pos]]){
dfs2(v,rot);
}
}
if(cnt==pos)
break;
}
}
void init(){
for(int i=1;i<=n;i++)
vis[i].reset();
memset(num,0,sizeof(num));
cnt=0;
memset(head,-1,sizeof(head));
}
int main() {
freopen("1.txt","r",stdin);
int cas;
scanf("%d",&cas);
while(cas--){
init();
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs(1,0);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&f[i]);
}
flag=0;
pos=1;
dfs2(1,-1);
flag?printf("YES\n"):printf("NO\n");
}
return 0;
}