一、题意

给出N个卡牌,卡牌的正反两面具有两个数字,取值范围为[1,2*n],给出若干个默认正面向上的卡牌,求最小反转多少张卡牌可以使得,每张卡牌朝上的面上都有一个不同的数字,同时满足最小反转次数的反转方法有多少个?

Alice and Bob are playing a card game. In this card game, a number is written on each face of the playing card. The rule of the game is described as follows:

- Alice arranges the cards in a row, and for each of the cards, she chooses one of its faces to place it up;
- Bob turns over minimum number of cards to make all numbers on the front faces unique.

They play the game some times, and Bob always succeeds making the numbers unique. However, both of them are not sure whether the number of cards flipped is minimum. Moreover, they want to figure out the number of different ways of turning over minimum number of cards to make the numbers unique. Two ways are considered equal if and only if the sets of flipped cards are equal. Please write a program to help them!

二、题解

考虑每张卡牌都有两个数字,要求必须选一个数字,则实际上可以考虑将卡牌表示成两个有向边——具有不同权重的边,权重表示反转的代价。具体来说,如果建立了边之后,对于每个联通快,如果有n个节点,且具有n-1或n个边,则可以给每条边分配一个节点——物理意义可以表示为给每张卡牌分配一个数字。对于其他情况则直接是不合法的。

考虑,n节点,n条边实际上是基环树,则分配时,可选的范围只有环的旋转方向(2个情况)。对于树结构,则可以自由地选择放弃使用哪个数字(n个情况)。

考虑暴力计算树DP统计以某个点为根的子树的权值和,实际上是一个O(N)的算法,则,直接求解复杂度为O(N2),不可接受。

考虑子树的定义,如果求出了父节点的DP值,且当前的DP值不包含父节点,则可以发现一个简单的状态转移方程:DP[NOW] = DP[FATHER] - EDGE(FATHER,NOW) - DP[NOW] + DP[CHILD] + EDGE[NOW,CHILD];这样做两遍遍历即可求出来我们要求的,以给定节点为根节点的权重值和,复杂度O(N)。

#include<bits/stdc++.h>
using namespace std; #define ll long long
const int MAXN = ;
const int MOD = ;
#define pp pair<int,int>
#define veci vector<int>
#define vecp vector<pp> int fa[MAXN],vis[MAXN],dp[MAXN];
vecp G[MAXN];
int n;
ll ans_cntt,ans_step,minn_cntt,minn_step; int cnt_e,cnt_v; void init_dfs(int now)
{
vis[now] = ;
int len = G[now].size();
for(int i=;i<len;++i)
{
int tar = G[now][i].first;
if(!vis[tar])init_dfs(tar);
}
cnt_v ++;
cnt_e += len;
} void tree_dp(int now,int father){
fa[now] = father;
int len = G[now].size();
dp[now] = ;
for(int i=;i<len;++i){
int tar = G[now][i].first;
int val = G[now][i].second;
if(father == tar)continue;
tree_dp(tar,now);
dp[now] += val + dp[tar];
}
} void dp_dfs(int now){
if(fa[now] == ){
if(minn_step > dp[now]){
minn_step = dp[now];
minn_cntt =;
}else if(minn_step == dp[now])minn_cntt ++; int len = G[now].size();
for(int i=;i<len;++i){
int tar = G[now][i].first;
dp_dfs(tar);
}
}else{
dp[now] = -dp[now];
int len = G[now].size();
for(int i=;i<len;++i){
int tar =G[now][i].first;
int val = G[now][i].second;
dp[now] += dp[tar] + val;
if(tar == fa[now])dp[now] -= !val;
}
if(minn_step > dp[now]){
minn_step = dp[now];
minn_cntt =;
}else if(minn_step == dp[now])minn_cntt ++; for(int i=;i<len;++i){
int tar =G[now][i].first;
if(tar == fa[now])continue;
dp_dfs(tar);
}
}
} veci cir_edge;
pp cir_vector;
int cir_val; bool find_circle(int now,int last){
vis[now] = ;
int len = G[now].size();
int cnt_last = ;
for(int i=;i<len;++i){
int tar = G[now][i].first;
int val = G[now][i].second;
if(tar == last && !cnt_last){
cnt_last++;
continue;
}
if(vis[tar] == ){
cir_vector = make_pair(now,tar);
cir_val = !val;
return true;
}
if(find_circle(tar,now))return true;
}return false;
} bool deal_circle(int now,int last,int target,int last_val){
int len =G[now].size();
int cnt_last = ;
int next = ;
for(int i=;i<len;++i){
int tar =G[now][i].first;
int val = G[now][i].second;
if(tar == last && cnt_last == && val == !last_val){
cnt_last++;
continue;
}
if(tar == target){
cir_edge.push_back(val);
next = tar;
break;
}
if(deal_circle(tar,now,target,val)){
cir_edge.push_back(val);
next = tar;
break;
}
}
if(next == )return false; for(int i=;i<len;++i){
int tar = G[now][i].first;
int val = G[now][i].second;
if(tar == last || tar == next ||tar == now)continue;
tree_dp(tar,now);
minn_step += dp[tar] + val;
}
return true;
} void init(){
memset(vis,,sizeof(vis));
for(int i=;i<*n+;++i)G[i].clear(); int succ = ; for(int i=;i<n;++i){
int a,b;
cin>>a>>b;
G[a].push_back(make_pair(b,));
G[b].push_back(make_pair(a,));
}
ans_cntt = ;
ans_step = ;
for(int i=;i<=*n;++i){
if(G[i].empty())continue;
if(vis[i])continue;
cnt_e = ;
cnt_v = ;
init_dfs(i);
cnt_e/=; // cout<<"check_cnt: "<<cnt_e<<" "<<cnt_v<<endl; if(cnt_e == cnt_v-){
minn_step = INT_MAX;
minn_cntt = ;
tree_dp(i,);
dp_dfs(i); ans_step += minn_step;
ans_cntt *= minn_cntt;
ans_cntt %=MOD;
continue;
}
if(cnt_e == cnt_v){
minn_step = ;
cir_edge.clear();
find_circle(i,);
deal_circle(cir_vector.first,cir_vector.second,cir_vector.first,cir_val);
int tmp1 = ,tmp2 = ;
int len = cir_edge.size();
for(int i=;i<len;++i){
tmp1 += cir_edge[i];
tmp2 += !cir_edge[i];
}
// cout<<"check_tmp: "<<tmp1<<" "<<tmp2<<endl;
ans_step += minn_step + min(tmp1,tmp2);
if(tmp1 == tmp2 )ans_cntt *= ;
ans_cntt %= MOD; continue;
}
succ = ;
break;
} if(succ){
cout<<ans_step<<" "<<ans_cntt<<"\n";
}else{
cout<<"-1 -1\n";
}
} int main(){ cin.sync_with_stdio(false);
int t;
cin>>t;
while(cin>>n)init(); return ;
}

HDU暑假多校第八场G-Card Game的更多相关文章

  1. HDU暑假多校第八场J-Taotao Picks Apples

    一.题意 给定一个序列,之后给出若干个修改,修改的内容为在原序列的基础上,将某一位元素的值改成给定的值<每次修改相互独立,不保存修改后的结果>.之后询问,在选择第一位元素的情况下,最长递增 ...

  2. HDU暑假多校第三场H.Monster Hunter

    一.题意 给定一个树状地图,每个树节点上有一只怪物,打死一只怪物的过程中将会消耗A点HP,打死之后将会获得B点HP.因为树状结构,所以每只怪物必须先打死父节点的怪兽之后在打死子节点的怪物.现在,给定每 ...

  3. HDU暑假多校第六场K-werewolf

    一.题意 好人必然说真话,坏人不一定说真话,给定N个人的言论<每人一个发言.不谈及自己>,要求指出有多少个人一定是好人,有多少个人一定是坏人.#define 狼人 坏人#define 村民 ...

  4. HDU暑假多校第四场J-Let Sudoku Rotate

    一.题意 Sudoku is a logic-based, combinatorial number-placement puzzle, which is popular around the wor ...

  5. 牛客多校第八场 G Gemstones 栈/贪心

    题意: 对于一个序列,把可以把连着三个相同的字母拿走,问最多拿走多少组. 题解: 直接模拟栈,三个栈顶元素相同则答案+1,并弹出栈 #include<bits/stdc++.h> usin ...

  6. 2020牛客暑假多校训练营 第二场 G Greater and Greater bitset

    LINK:Greater and Greater 确实没能想到做法. 考虑利用bitset解决问题. 做法是:逐位判断每一位是否合法 第一位 就是 bitset上所有大于\(b_1\)的位置 置为1. ...

  7. [HDU6304][数学] Chiaki Sequence Revisited-杭电多校2018第一场G

    [HDU6304][数学] Chiaki Sequence Revisited -杭电多校2018第一场G 题目描述 现在抛给你一个数列\(A\) \[ a_n=\begin{cases}1 & ...

  8. 牛客多校第三场 G Removing Stones(分治+线段树)

    牛客多校第三场 G Removing Stones(分治+线段树) 题意: 给你n个数,问你有多少个长度不小于2的连续子序列,使得其中最大元素不大于所有元素和的一半 题解: 分治+线段树 线段树维护最 ...

  9. 2019牛客多校第八场 F题 Flowers 计算几何+线段树

    2019牛客多校第八场 F题 Flowers 先枚举出三角形内部的点D. 下面所说的旋转没有指明逆时针还是顺时针则是指逆时针旋转. 固定内部点的答案的获取 anti(A)anti(A)anti(A)或 ...

随机推荐

  1. laravel5.4学习笔记

    1.安装laravel可以直接用composer安装,然后用laravel new xxx来新建项目 服务器上安装了composer(php包管理工具)以后, composer global requ ...

  2. zip4j之加压解压

    最近看同事搞个文件打包,搞了大半天,还是有问题!嗨~~ 网上明明有现成的,偏偏要自己写! 下面是基于zip4j实现加压解决的简单工具类 package com.learcher.zip; import ...

  3. Uva 11294 婚姻

    题目链接:https://vjudge.net/contest/166461#problem/C 题意: n对夫妻,有m对人吵过架,不能排在同一边,求新娘的一边的人: 分析: 每对夫妻,看成两个点,女 ...

  4. python nmap模块 端口探测

    今天添加端口探测功能,主要实现方式是通过nmap模块调用,扫描1-65535端口.上一篇中已经将UP的PC机全部获取到,这里直接从已知在线的PC中进行端口扫描就可以了,会节省很多时间. 代码如下,还是 ...

  5. cascade DecodeBBox层

    https://zhuanlan.zhihu.com/p/36095768 我的推断,第二第三阶段应该不是把所有anchor进行bounding box regression,然后再选取当前条件下的所 ...

  6. MVC学习七:Razor布局之加载分部视图【PartialView】

    Partial View 顾名思义就是Html代码片段,应用于此HTML代码多次被页面加载时使用.(类似于WebForm程序中的用户控件) 注:PartialView和正常的View页面在访问时没有任 ...

  7. 【题解】洛谷P1495 曹冲养猪 (中国剩余定理)

    洛谷P1495:https://www.luogu.org/problemnew/show/P1495 思路 建立了a个猪圈 有b头猪没有去处 即x≡b(mod a) x即是ans 把所有的关系全部列 ...

  8. Java面向对象知道这些就够了

    面向对象 面向对象是一种思维方式,相对于面向过程而言的. 面向过程在流程中关注动作执行的每一个细节 — 自己动手做 面向对象重点找这个对象,只要找到了对象,那么这个对象所具有的功能就能够被使用 — 找 ...

  9. Autofac4.0以上的版本通过json配置文件方式实现IOC的MVC5设置

    我们知道java用到了spring来实现IOC,而我们学习的.net也有.net spring.但是.net spring现在没人维护了,进公司后发现公司使用到了autofac.但是用的是3.X的版本 ...

  10. libcurl 基本使用

    libcurl,在HLS流媒体播放终端上提供HTTP下载的相关接口.具体的使用方式可以参见http://curl.haxx.se/libcurl/c/libcurl-tutorial.html,或博客 ...