@description@

给定一个n个点,m条边的无向图,其中你在第i个点建立旅游站点的费用为C[i]。在这张图中,任意两点间不存在节点数超过10的简单路径。

请找到一种费用最小的建立旅游站点的方案,使得每个点要么建立了旅游站点,要么与它有边直接相连的点里至少有一个点建立了旅游站点。

Input

第一行包含两个正整数n,m(1<=n<=20000,0<=m<=25000),分别表示点数和边数。

第二行包含n个整数,其中第i个数为Ci,表示在第i个点建立旅游站点的费用。

接下来m行,每行两个正整数u,v(1<=u,v<=n),表示u与v之间连了一条边,保证没有重边

Output

输出一行一个整数,即最小的总费用。

Sample Input

6 6

3 8 5 6 2 2

1 2

2 3

1 3

3 4

4 5

4 6

Sample Output

7

Explanation

分别在1,5,6号站点建立旅游站点。

@solution@

如果在树上做,这道题就是道树 dp 入门题。

如果在一般的图上做,这道题就是道 np 问题。

但是我们有一个 “任意两点间不存在节点数超过10的简单路径” 的条件,这使我们联想到状压/搜索。

考虑将图转为树,建出每一个连通子图(注意不一定整张图连通)的 dfs 树,由上面那个条件我们有树的深度 <= 10。

考虑 dfs 树的性质:边要么是树边要么是返祖边。这意味着一个点连向它祖先的边 <= 10。我们或许可以状压它所有祖先的状态。

定义状态 dp[i][s] 表示考虑 dfs 序的前 i 个元素,第 i 个元素到根的路径上所有点的状态为 s。其中根据我们树 dp 入门题的思想,将 s 的状态设为三进制,分别表示 “0 -不选且相邻点也都不选”,“1 - 不选但已经存在相邻点选”,“2 - 选”。

考虑从 dp[i][s] 转移到 dp[i+1][s']。这个过程可以看作退栈,将栈顶的不合法元素全部弹出后再在栈中加入新的元素。

首先要保证 s 中不是 i+1 祖先的点为 1 or 2,过后将它们从状态 s 中删除。转移考虑 i+1 选不选,再考虑这个决策对返祖边的影响:如果选,返祖边的 0 状态全部改为 1 状态;如果不选,如果返祖边存在一个 2 状态则 i + 1 为 1 状态,否则为 0 状态。

时间复杂度 O(3^10*(n + m))。虽然很不科学不过应该跑得挺快的。

(但我常数大加了随机选dfs树根+二进制代替三进制+转移的一点优化才过)(但我总感觉是因为加了这些东西才跑得慢)

@accepted code@

#include<cstdio>
#include<vector>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 40000;
const int MAXM = 50000;
const int SIZE = (1<<10);
const int INF = (1<<30);
struct Graph{
struct edge{
int to; edge *nxt;
}edges[2*MAXM + 5], *adj[MAXN + 5], *ecnt;
Graph() {ecnt = &edges[0];}
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
}G1, G2;
vector<int>vec[MAXN + 5];
int dep[MAXN + 5], dfn[MAXN + 5], tid[MAXN + 5], dcnt = 0;
void dfs(int x, int f) {
dep[x] = dep[f] + 1, tid[x] = (++dcnt), dfn[dcnt] = x;
for(Graph::edge *p=G1.adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
if( !tid[p->to] )
G2.addedge(x, p->to), dfs(p->to, x);
else {
if( tid[p->to] < tid[x] )
vec[x].push_back(p->to);
}
}
}
int C[MAXN + 5];
int dp[2][SIZE + 5][SIZE + 5];
void init(int t1, int t2) {
for(int s1=0;s1<t1;s1++) {
int s2 = s1;
do {
dp[1][s1][s2] = dp[0][s1][s2];
if( !s2 ) break;
s2 = s1 & (s2 - 1);
}while( true );
}
for(int s1=0;s1<t2;s1++) {
int s2 = s1;
do {
dp[0][s1][s2] = INF;
if( !s2 ) break;
s2 = s1 & (s2 - 1);
}while( true );
}
}
int get_ans(int x, int m) {
int lst = 0; dp[0][0][0] = 0;
for(int p=tid[x];p<=tid[x]+m-1;p++) {
int i = dfn[p];
init(1<<lst, 1<<dep[i]);
int t1 = (1<<(dep[i]-1)), t2 = ((1<<(lst-(dep[i]-1)))-1)<<(dep[i]-1), t3 = t1>>1;
for(int j=0;j<vec[i].size();j++)
t3 |= (1<<(dep[vec[i][j]]-1));
for(int s3=0;s3<=t2;s3+=t1) {
for(int s1=0;s1<t1;s1++) {
int s2 = s1;
do {
int s4 = s1|t2, s5 = s2|s3;
if( dp[1][s4][s5] != INF ) {
if( s5 & t3 ) dp[0][s1|t1][s2] = min(dp[0][s1|t1][s2], dp[1][s4][s5]);
else dp[0][s1][s2] = min(dp[0][s1][s2], dp[1][s4][s5]);
dp[0][s1|t3|t1][s2|t1] = min(dp[0][s1|t3|t1][s2|t1], dp[1][s4][s5] + C[i]);
}
if( !s2 ) break;
s2 = s1 & (s2 - 1);
}while( true );
}
}
lst = dep[i];
}
int t = (1<<lst), ret = INF;
for(int i=0;i<t;i++)
ret = min(ret, dp[0][t-1][i]);
return ret;
}
int fa[MAXN + 5], siz[MAXN + 5];
int find(int x) {return fa[x] = (fa[x] == x ? x : find(fa[x]));}
void unite(int x, int y) {
int fx = find(x), fy = find(y);
if( fx != fy )
fa[fx] = fy, siz[fy] += siz[fx];
}
vector<int>v[MAXN + 5];
int main() {
srand(20040911);
int n, m; scanf("%d%d", &n, &m);
for(int i=1;i<=n;i++)
scanf("%d", &C[i]), fa[i] = i, siz[i] = 1;
for(int i=1;i<=m;i++) {
int u, v; scanf("%d%d", &u, &v);
G1.addedge(u, v), unite(u, v);
}
int ans = 0;
for(int i=1;i<=n;i++)
v[find(i)].push_back(i);
for(int i=1;i<=n;i++)
if( fa[i] == i ) {
int rt = v[i][((unsigned int)(rand() << 16 | rand())) % v[i].size()];
dfs(rt, 0), ans += get_ans(rt, siz[i]);
}
printf("%d\n", ans);
}

@details@

我现在还是感觉是因为我加了些奇奇怪怪的优化才跑得非常慢。

人家跑 6000ms,可我要跑 13000ms 左右。。。一开始还 T 了几发。。。

果然有些东西还是不要乱用。

@bzoj - 3836@ [Poi2014]Tourism的更多相关文章

  1. 【刷题】BZOJ 4543 [POI2014]Hotel加强版

    Description 同OJ3522 数据范围:n<=100000 Solution dp的设计见[刷题]BZOJ 3522 [Poi2014]Hotel 然后发现dp的第二维与深度有关,于是 ...

  2. 主席树||可持久化线段树||BZOJ 3524: [Poi2014]Couriers||BZOJ 2223: [Coci 2009]PATULJCI||Luogu P3567 [POI2014]KUR-Couriers

    题目:[POI2014]KUR-Couriers 题解: 要求出现次数大于(R-L+1)/2的数,这样的数最多只有一个.我们对序列做主席树,每个节点记录出现的次数和(sum).(这里忽略版本差值问题) ...

  3. BZOJ 3836 Codeforces 280D k-Maximum Subsequence Sum (模拟费用流、线段树)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=3836 (Codeforces) http://codeforces.com ...

  4. BZOJ 3524: [Poi2014]Couriers [主席树]

    3524: [Poi2014]Couriers Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1892  Solved: 683[Submit][St ...

  5. BZOJ 3524: [Poi2014]Couriers

    3524: [Poi2014]Couriers Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1905  Solved: 691[Submit][St ...

  6. Bzoj 3831 [Poi2014]Little Bird

    3831: [Poi2014]Little Bird Time Limit: 20 Sec Memory Limit: 128 MB Submit: 310 Solved: 186 [Submit][ ...

  7. [BZOJ 3829][POI2014] FarmCraft

    先贴一波题面... 3829: [Poi2014]FarmCraft Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 421  Solved: 197[ ...

  8. BZOJ 3526: [Poi2014]Card

    3526: [Poi2014]Card Time Limit: 25 Sec  Memory Limit: 64 MBSubmit: 267  Solved: 191[Submit][Status][ ...

  9. BZOJ.3522.[POI2014]Hotel(DP)

    题目链接 BZOJ 洛谷 以为裸点分治,但数据范围怎么这么小?快打完了发现不对.. n^2做的话其实是个水题.. 枚举每一个点为根,为了不重复计算,我们要求所求的三个点必须分别位于三棵子树上. 考虑当 ...

随机推荐

  1. vue 使用 element ui动态添加表单

    html部分 <div class="hello"> <el-form :model="dynamicValidateForm" ref=&q ...

  2. vue-eslint配置文件

    做项目的时候,我把eslint设置为了false,可想而知提交会产生的冲突 让我一个一个解决肯定不可能的,eslint的rule很多 在vue的配置文件.eslintrc.js中配置以下选项 这样只需 ...

  3. windows 操作系统下git报filename too long 处理方法

    两种方法解决: 一是通过修改配置文件 [core] repositoryformatversion = filemode = false bare = false logallrefupdates = ...

  4. callee和caller属性的区别

    在函数内部,有两个特殊的对象:arguments和this .arguments是一个类数组对象,用于存放传入函数中的所有参数. callee是arguments对象的属性,caller是所有函数对象 ...

  5. php require_once的使用方法

    学习笔记 require_once 语句和 require 语句完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含. equire_once() 为了避免重复加载文件. ...

  6. Hdu 1299

    Diophantus of Alexandria Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java ...

  7. Linux操作系统各版本ISO镜像下载(包括oracle linux\redhat\centos\u

    Linux操作系统各版本ISO镜像下载(包括oracle linux\redhat\centos\ubuntu\debian等) 1.Oracle Linux(下载地址) (1)OracleLinux ...

  8. 配置了两天python【python可以的】

    首先是看cs231n 发现代码的版本是py2 而我只装了 py3(anaconda3) 怎么办呢 于是想办法装了 anaconda2 并与之共存 ,调用的时候用 activate py2调用 http ...

  9. Codeforces 293B Distinct Paths DFS+剪枝+状压

    目录 题面 题目链接 题意翻译 输入输出样例 输入样例#1 输出样例#1 输入样例#2 输出样例#2 输入样例#3 输出样例#3 输入样例#4 输出样例#4 说明 思路 AC代码 总结 题面 题目链接 ...

  10. R语言因子

    R语言因子 因子是它们用于将数据进行分类并将其存储为级别的数据对象.它们可以同时存储字符串和整数.它们在具有唯一值的有限数目的列是有用的. 例如,"male, "Female&qu ...