题解 P6542 【[NOI2004]沙丘】
题意简述
-
- 你可以:查看当前点的状况(相邻点数和是否有路标);在一个点上拿起/放下路标;走到一个相邻节点。
- 特别注意的是:通过一条边走到相邻节点时,边的编号是临时确定的。即:把进入这个点时经过的边设为
0 号,逆时针依次确定每一条边的编号。 -
分析
思维难度较高的题。
容易想到一种......
。
。
。
然后发现什么方法都想不到。
从最简单的地方开始考虑:我们肯定需要遍历这张图,这样才能确定
然后马上出现了一坨问题:如何确定当前点是以前没遍历到的?如何确定每条边的相对位置?如何确定每个点在其父亲的儿子中的位置?
确定边的相对位置: 假设我们现在走到了
这样,我们也可以确定当前子节点在父亲的相对位置了。
确定新点:假设我们现在走到
还有一种情况,就是
为帮助理解过程,手玩一下样例的前半部分:
我们初始在
于是我们花了这么长的时间,终于把前三个点做出来了。考虑这样做的复杂度,无非是走每个点的时候都要回到根再回去。所以可能大概也许是
据说我码风奇异:
void init();
void look(int&, bool&);
void put_sign();
void take_sign();
void walk(int);
void report(int, int);
#include<bits/stdc++.h>
using namespace std;
int d,n,m;
int ar[1001],fl[1001][1001],fa[1001],ns[1001];
bool si;
void dfs(int x){
look(d,si);bool nfl=1;
for(int i=(x!=1);i<d;i++,look(d,si)){
if(fl[x][i]){
if(!nfl)walk(1),walk(0);
continue;
}
ar[x]=i;
if(nfl)walk(i);
else walk(1);
put_sign(),walk(0),m++;
int ni=x;
while(!si&&ni!=1)walk(d-ar[ni]),ni=fa[ni],look(d,si);
if(!si){
fa[++n]=x,ns[x]=n;
while(ns[ni])walk((ni==1)?0:ar[ni]),ni=ns[ni];
take_sign(),dfs(n),nfl=0;
}else{
take_sign();int ng=ni;
while(ni!=x)walk((ni==ng)?0:ar[ni]),ni=ns[ni];
int nd=0,nj=0,tmp=0;
put_sign(),walk(0),ni=fa[ni],look(d,si);
while(ni!=ng)walk(d-ar[ni]),ni=fa[ni],look(d,si);
bool nsi=0;
look(nd,nsi);
for(nj=1;nj<=nd;nj++){
walk(1),look(tmp,nsi);
if(nsi)take_sign();
walk(0);
if(nsi)break;
}
fl[ni][(ar[ni]+nj)%nd]=1,walk(nd-nj),ni=ns[ni],nfl=1;
while(ni!=x)walk(ar[ni]),ni=ns[ni];
}
}
look(d,si),walk(!nfl),ns[x]=ar[x]=0;
}
int main(){
init();
n++;
dfs(1);
report(n,m);
return 0;
}
没错,这题其实只是图上DFS