【CF453C】Little Pony and Summer Sun Celebration——杨子曰题目

【CF453C】Little Pony and Summer Sun Celebration——杨子曰题目

题目描述
Twilight Sparkle learnt that the evil Nightmare Moon would return during the upcoming Summer Sun Celebration after one thousand years of imprisonment on the moon. She tried to warn her mentor Princess Celestia, but the princess ignored her and sent her to Ponyville to check on the preparations for the celebration.
在这里插入图片描述
Twilight Sparkle wanted to track the path of Nightmare Moon. Unfortunately, she didn’t know the exact path. What she knew is the parity of the number of times that each place Nightmare Moon visited. Can you help Twilight Sparkle to restore any path that is consistent with this information?

Ponyville can be represented as an undirected graph (vertices are places, edges are roads between places) without self-loops and multi-edges. The path can start and end at any place (also it can be empty). Each place can be visited multiple times. The path must not visit more than 4n 4n places.

输入格式:
The first line contains two integers n and m ( 2   ≤   n   ≤   1 0 5 ; 0   ≤   m   ≤   1 0 5 2 ≤ n ≤ 10^5; 0 ≤ m ≤ 10^5 2n105;0m105) — the number of places and the number of roads in Ponyville. Each of the following m lines contains two integers ui, vi (1 ≤ ui, vi ≤ n; ui ≠ vi), these integers describe a road between places u i u_i ui and v i v_i vi.

The next line contains n integers: x 1 ,   x 2 ,   . . . ,   x n ( 0   ≤   x i   ≤   1 ) x_1, x_2, ..., x_n (0 ≤ x_i ≤ 1) x1,x2,...,xn(0xi1)— the parity of the number of times that each place must be visited. If xi = 0, then the i-th place must be visited even number of times, else it must be visited odd number of times.

输出格式:
Output the number of visited places k in the first line (0 ≤ k ≤ 4n). Then output k integers — the numbers of places in the order of path. If x i   =   0 x_i = 0 xi=0, then the i-th place must appear in the path even number of times, else i-th place must appear in the path odd number of times. Note, that given road system has no self-loops, therefore any two neighbouring places in the path must be distinct.

If there is no required path, output -1. If there multiple possible paths, you can output any of them.

输入样例#1:

3 2
1 2
2 3
1 1 1

输出样例#1:

3
1 2 3

输入样例#2:

5 7
1 2
1 3
1 4
1 5
3 4
3 5
4 5
0 1 0 1 0

输出样例#2:

10
2 1 3 4 5 4 5 4 3 1 

输入样例#3:

2 0
0 0

输出样例#3:

0

这道题(还有这道题的那场比赛)为什么有一个如此奇怪背景(小马宝莉???)


这道题就是给你一张图,给出每个点经过的次数的奇偶性(0表示偶数,1表示奇数),让你找出一条长度小于4n(n是点的个数)的满足要求的路径


绝对是一道思维好题

我们先来这样想:最一开始的时候所有点经过的次数都是0,也就是所有点都经过偶数次,我们都用0表示,然后我们每经过一个节点,都把这个点上的值取反,这个值就表示了这个点经过次数的奇偶性

现在我们最开始的状态全是0,然后我们还有目标状态,这样很难思考,我们稍稍转化一下,我们能不能从目标状态开始,然后想办法把每个节点的值都想办法给弄成0捏?当然可以,根据取反的性质这种想法是完全可行滴

接下来我们就来思考如何把这张图上的每个节点的值都弄成0

我们从随便一个点开始DFS下去,走过的节点我们就不会走了(除了回溯到它来的那个节点),这样一来我们就形成了一颗DFS树,我们来考虑现在所在的节点,我们已经把它DFS树里的子节点都走过了,现在是回溯到这个节点,此时我们保证了它子树里的节点全是0,接下来我们要看这个节点是0,还是1,如果是0,美滋滋,完美维护,我们直接回溯

BUT,如果是1的话我们得想办法把它多经过一次,这样来做一个粗暴的操作:先去它的爸爸,再回到它,然后你就发现这个节点已经成功地被我们弄成了0,而且没有改变它的子树,然后我们再回溯去处理它的爸爸就好了

有一个细节还需要注意:就是如果我们回溯到了根节点但是它的值还是1肿么办捏?,我们就少走一次,也就是就是说最后一个回溯到它的儿子,我们就不要回溯了,这样我们就完美地保证了根节点也是0

但是题目中还有一个条件要求路径的长度小于4n这一点我们可以保证吗?——完全可以

对于每一个节点它可能会过去一次回来一次,这样就有2n的代价了,我们再来考虑为了维护当前节点是0而多走的次数,总体来看每次我们的维护操作都是2的代价,每个节点最多维护一次,这样一来总长度是一定不会超过4n的

于是你有没有发现,除了图不连通,而且每个连通块里都有点是1的这种情况是无解的外,其他情况都是必定有解的,美滋滋~~


打代码时注意:

  • 我们真正在代码中判断根节点的最后一条边要不要回溯应该这样来做:我们把根节点的爸爸设成一个空节点,然后DFS的过程中如果根节点需要维护,我们还是让它维护,然后最外面需要输出的时候看一下答案序列里倒数第二个是不是空节点,如果时就说明最后回溯的那条边我们需要扔掉,那我们最后经过的三个点不要输出(分别对应维护根节点的一来一回,以及最后回溯上来的那条边)
  • 我们在选择最开始DFS的的根节点的时候可以选择一个值为1的点,这样的话一遍DFS完以后可以判断有没有值为1点和它不在同一个连通块(无解),而且保证了有多个连通块而且有解时(只有一个连通块里有值为1的节点),我们DFS的一定是需要走的那个连通块

c++代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;

struct Edge{
    int next,to;
}edge[maxn*2];

vector<int> ans;

int n,m,nedge=0;
int head[maxn],x[maxn],vis[maxn];

void addedge(int a,int b){
    edge[nedge].to=b;
    edge[nedge].next=head[a];
    head[a]=nedge++;
}

void dfs(int v,int fa){
    vis[v]=1;
    x[v]^=1;
    ans.push_back(v);
    for (int i=head[v];i!=-1;i=edge[i].next){
        int u=edge[i].to;
        if (vis[u]) continue;
        dfs(u,v);
        ans.push_back(v);
        x[v]^=1;
    }
    if (x[v]){
        ans.push_back(fa);
        x[fa]^=1; 
        ans.push_back(v);
        x[v]^=1;
    }
}

int main(){
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        addedge(a,b);
        addedge(b,a);
    }
    for (int i=1;i<=n;i++){
        scanf("%d",&x[i]);
    }
    int r=-1;
    for (int i=1;i<=n;i++){
        if (x[i]){
            r=i;
            break;
        }
    }
    if (r==-1){
        printf("0");
        return 0;
    }
    dfs(r,0);
    for (int i=1;i<=n;i++){
        if (x[i] && !vis[i]){
            printf("-1");
            return 0;
        }
    }
    if (ans[ans.size()-2]==0){
        printf("%d\n",ans.size()-3);
        for (int i=0;i<ans.size()-3;i++){
            printf("%d ",ans[i]);
        }
    }
    else{
        printf("%d\n",ans.size());
        for (int i=0;i<ans.size();i++){
            printf("%d ",ans[i]);
        }
    }
    return 0;
} 

参考:
https://blog.csdn.net/seven_jun/article/details/51911216

https://blog.csdn.net/largecub233/article/details/58073303

感谢可耐滴小慕容的讲解

于HG机房

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值