zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:卡常题/b(基环树+DP)

    题目描述

    $ρ$有一个二分连通无向图,$X$方点、$Y$方点均为$n$个(编号为$1sim n$)。
    这个二分图比较特殊,每一个$Y$方点的度为$2$,一条黑色边,一条白色边。
    所有黑色边权值均为$a$,所有白色边权值均为$b$。
    选择一个$X$方点,代价为连接的所有边的权值之和。
    激活一个$Y$方点,需要选择至少一个与之相邻的$X$方点。
    现在,$ρ$想激活每个$Y$方点,他想知道最小的总代价。
    不过$ρ$很善良,他给你开了$O2$优化。
    这样你就不会被卡常了。
    当然,除非你真的连读入优化都不想写,或者常数真的丑死。


    输入格式

    第一行:三个正整数$n$、$a$、$b$。
    接下来$n$行:每行两个正整数,第$i$行表示第$i-1$个$Y$方点的黑色边连接的$X$方点,白色边连接的$X$方点。


    输出格式

    第一行:一个整数,代表最小的总代价。


    样例

    样例输入:

    4 2 3
    1 2
    3 1
    1 4
    2 3

    样例输出:

    12


    数据范围与提示

    $20\%$的数据:$nleqslant 20$。
    $40\%$的数据:$nleqslant {10}^3$。
    另外$10\%$的数据:$a=b=1$。
    另外$20\%$的数据:保证每个$X$方点连接的边颜色相同。
    另外$10\%$的数据:保证原图是一个大小为$2 imes n$的环。
    $100\%$的数据:$nleqslant {10}^6,a,bleqslant 100$,保证无重边。


    题解

    发现这张图其实就是一个基环树,所以我们可以做基环树$DP$。

    首先,找到这个环,然后删掉这条边,这样就变成了一棵树了,向两个方向进行$DP$,取较小的即可。

    时间复杂度:$Theta(n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    using namespace std;
    struct rec{int nxt,to;}e[2000001];
    struct node{int i,l,r;}root;
    int head[1000001],cnt=1;
    int n,a,b;
    int sam[1000001];
    int dp[2][1000001][2];
    bool vis[1000001];
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void pre_dfs(int x,int fa)
    {
    	vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		if(e[i].to==fa)continue;
    		if(vis[e[i].to]){root=(node){i,x,e[i].to};return;}
    		pre_dfs(e[i].to,x);
    	}
    }
    void DP0(int x,int fa)
    {
    	int sum=0;
    	dp[0][x][0]=0;
    	dp[0][x][1]=sam[x];
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		if(i==root.i||(i^1)==root.i||e[i].to==fa)continue;
    		DP0(e[i].to,x);
    		sum+=min(dp[0][e[i].to][0],dp[0][e[i].to][1]);
    		dp[0][x][0]+=dp[0][e[i].to][1];
    	}
    	dp[0][x][1]+=sum;
    }
    void DP1(int x,int fa)
    {
    	int sum=0;
    	dp[1][x][0]=0;
    	dp[1][x][1]=sam[x];
    	for(int i=head[x];i;i=e[i].nxt)
    	{
    		if(i==root.i||(i^1)==root.i||e[i].to==fa)continue;
    		DP1(e[i].to,x);
    		sum+=min(dp[1][e[i].to][0],dp[1][e[i].to][1]);
    		dp[1][x][0]+=dp[1][e[i].to][1];
    	}
    	dp[1][x][1]+=sum;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&a,&b);
    	for(int i=1;i<=n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		sam[x]+=a;sam[y]+=b;
    		add(x,y);add(y,x);
    	}
    	pre_dfs(1,0);
    	DP0(root.l,0);
    	DP1(root.r,0);
    	cout<<min(dp[0][root.l][1],dp[1][root.r][1])<<endl;
    	return 0;
    }
    

    rp++

  • 相关阅读:
    学习redis-安装和基本一些命令
    Eclipse启动报错Java was started but returned exit code=13
    踩过的坑系列之InputStream.read(byte[])方法
    <<深入Java虚拟机>>-虚拟机类加载机制-学习笔记
    <<深入Java虚拟机>>-第三章-垃圾收集器与内存分配策略-学习笔记
    <<深入Java虚拟机>>-第二章-Java内存区域-学习笔记
    创建线程的两种方式比较Thread VS Runnable
    Java设计模式之--代理模式学习
    shell脚本中$参数的介绍
    (转)使用DataTime这个类来获取当前的时间
  • 原文地址:https://www.cnblogs.com/wzc521/p/11498628.html
Copyright © 2011-2022 走看看