题目性质比较显然,相同颜色联通块可以合并成一个点,重新建树后,发现相邻两个点的颜色一定是不一样的。

然后发现,对于一条链来说,每次把一个点反色,实际上使点数少了2个。如下图

而如果一条链上面有分支,也是一样:

所以我们实际上只需要把最长链上的变成一种颜色就可以了。最长链就是直径,需要改动的点就是$\frac{tot+1}{2}$,$tot$就是直径的点数。

(话说$stl$好慢aaa!!!要克制住我自己少用$map$叻!

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std; int n, a[];
int stot, tov[], nex[], h[];
map < pair < int, int >, int > G; void add ( int u, int v ) {
tov[++stot] = v;
nex[stot] = h[u];
h[u] = stot;
} int stot_co, tov_co[], nex_co[], h_co[];
void add_co ( int u, int v ) {
tov_co[++stot_co] = v;
nex_co[stot_co] = h_co[u];
h_co[u] = stot_co;
} int fa[];
int find ( int x ) {
if ( x != fa[x] ) fa[x] = find ( fa[x] );
return x;
} void unionn ( int x, int y ) {
int xx = find ( x ), yy = find ( y );
fa[xx] = yy;
} int opt, color[];
void dfs1 ( int u, int f ) {
for ( int i = h[u]; i; i = nex[i] ) {
int v = tov[i];
if ( v == f ) continue;
color[v] = ;
if ( a[v] == a[u] ) color[v] = color[u];
else color[v] = ++ opt;
dfs1 ( v, u );
int x = find ( color[v] ), y = find ( color[u] );
if ( color[v] != color[u] && x != y ) {
add_co ( color[v], color[u] );
add_co ( color[u], color[v] );
unionn ( x, y );
}
}
} int dis[], rt;
void dfs ( int u, int f ) {
for ( int i = h_co[u]; i; i = nex_co[i] ) {
int v = tov_co[i];
if ( v == f ) continue;
dis[v] = dis[u] + ;
dfs ( v, u );
}
if ( dis[u] > dis[rt] ) rt = u;
} int main ( ) {
freopen ( "color.in", "r", stdin );
freopen ( "color.out", "w", stdout );
int T;
scanf ( "%d", &T );
while ( T -- ) {
G.clear ( );
stot = , stot_co = , opt = ;
scanf ( "%d", &n );
for ( int i = ; i <= n; i ++ ) h[i] = ;
for ( int i = ; i <= n; i ++ ) h_co[i] = ;
for ( int i = ; i <= n; i ++ ) color[i] = ;
for ( int i = ; i <= n; i ++ ) scanf ( "%d", &a[i] );
for ( int i = ; i < n; i ++ ) {
int u, v;
scanf ( "%d%d", &u, &v );
add ( u, v );
add ( v, u );
}
for ( int i = ; i <= n; i ++ ) fa[i] = i;
color[] = ++opt;
dfs1 ( , );
rt = ;
for ( int i = ; i <= opt; i ++ ) dis[i] = ;
dfs ( , );
for ( int i = ; i <= opt; i ++ ) dis[i] = ;
dfs ( rt, );
printf ( "%d\n", ( dis[rt] + ) / );
}
return ;
}

小模拟,考试的时候觉得状态太多,不可能重复???然后就没有写$vis$打标记,下来一加就...(辛酸泪QAQ

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
using namespace std; int n, t[], G[][];
map < int, int > mx, my; struct node {
int x, y, opt, id;
node ( int x = , int y = , int opt = , int id = ) :
x ( x ), y ( y ), opt ( opt ), id ( id ) { }
};
queue < node > q;
int vis[][][][]; struct QAQ {
int x, y;
QAQ ( int x = , int y = ) :
x ( x ), y ( y ) { }
}; QAQ print ( int x, int y, int opt, int id ) {
for ( int i = ; i <= t[id]; i ++ ) {
G[x][y] = ;
x = x + mx[opt]; y = y + my[opt];
}
return QAQ ( x - mx[opt], y - my[opt] );
} void BFS ( int sx, int sy ) {
q.push ( node ( sx, sy + t[] - , , ) );
QAQ a = print ( sx, sy, , );
vis[sx][sy+t[]-][][] = ;
while ( !q.empty ( ) ) {
node x = q.front ( ); q.pop ( );
if ( x.id == n ) break;
if ( x.opt != - && x.opt != ) {
int L = x.opt - , R = x.opt + ;
int id = x.id + ;
int lsx = x.x + mx[L], lsy = x.y + my[L];
int rsx = x.x + mx[R], rsy = x.y + my[R];
QAQ ll = print ( lsx, lsy, L, id );
QAQ rr = print ( rsx, rsy, R, id );
if ( !vis[ll.x][ll.y][id][L] ) {
q.push ( node ( ll.x, ll.y, L, id ) );
vis[ll.x][ll.y][id][L] = ;
}
if ( !vis[rr.x][rr.y][id][R] ) {
q.push ( node ( rr.x, rr.y, R, id ) );
vis[rr.x][rr.y][id][R] = ;
}
} else if ( x.opt == - ) {
int L = , R = x.opt + ;
int id = x.id + ;
int lsx = x.x + mx[L], lsy = x.y + my[L];
int rsx = x.x + mx[R], rsy = x.y + my[R];
QAQ ll = print ( lsx, lsy, L, id );
QAQ rr = print ( rsx, rsy, R, id );
if ( !vis[ll.x][ll.y][id][L] ) {
q.push ( node ( ll.x, ll.y, L, id ) );
vis[ll.x][ll.y][id][L] = ;
}
if ( !vis[rr.x][rr.y][id][R] ) {
q.push ( node ( rr.x, rr.y, R, id ) );
vis[rr.x][rr.y][id][R] = ;
}
} else if ( x.opt == ) {
int L = x.opt - , R = -;
int id = x.id + ;
int lsx = x.x + mx[L], lsy = x.y + my[L];
int rsx = x.x + mx[R], rsy = x.y + my[R];
QAQ ll = print ( lsx, lsy, L, id );
QAQ rr = print ( rsx, rsy, R, id );
if ( !vis[ll.x][ll.y][id][L] ) {
q.push ( node ( ll.x, ll.y, L, id ) );
vis[ll.x][ll.y][id][L] = ;
}
if ( !vis[rr.x][rr.y][id][R] ) {
q.push ( node ( rr.x, rr.y, R, id ) );
vis[rr.x][rr.y][id][R] = ;
}
}
}
} int main ( ) {
freopen ( "grow.in", "r", stdin );
freopen ( "grow.out", "w", stdout );
scanf ( "%d", &n );
mx[] = , mx[] = , mx[] = , mx[] = , mx[] = , mx[-] = -, mx[-] = -, mx[-] = -;
my[] = , my[] = , my[] = , my[] = -, my[] = -, my[-] = , my[-] = , my[-] = -;
for ( int i = ; i <= n; i ++ ) scanf ( "%d", &t[i] );
BFS ( , );
int ans = ;
for ( int i = ; i < ; i ++ )
for ( int j = ; j < ; j ++ )
if ( G[i][j] ) ans ++;
printf ( "%d", ans );
}

有关串用$dp$解决是很显然的(?$idy$题解原话),定义$dp[s][t]$表示从$s$状态转移到$t$状态最少的修改数。关于状态定义代码有注释。用线段树维护区间状态转移$dp$值,每个节点保存一个矩阵,节点合并时类似$floyed$,枚举断点转移。查询时查询区间即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#define oo 0x3f3f3f3f
using namespace std; // 0 1 2 3 4
// $ 2 20 201 2017 const int N = ; struct Info {
int dp[][];
void init ( int cur ) {
memset ( dp, 0x3f, sizeof ( dp ) );
if ( cur == || cur == || cur == || cur == || cur == ) {
for ( int i = ; i <= ; i ++ )
dp[i][i] = ;
} else if ( cur == ) {
dp[][] = ; dp[][] = ;
dp[][] = dp[][] = dp[][] = dp[][] = ;
} else if ( cur == ) {
dp[][] = ;
dp[][] = ;
dp[][] = dp[][] = dp[][] = dp[][] = ;
} else if ( cur == ) {
dp[][] = ;
dp[][] = ;
dp[][] = dp[][] = dp[][] = dp[][] = ;
} else if ( cur == ) {
dp[][] = ;
dp[][] = ;
dp[][] = dp[][] = dp[][] = dp[][] = ;
} else if ( cur == ) {
dp[][] = ;
dp[][] = ;
dp[][] = dp[][] = dp[][] = ;
}
}
}; Info operator + ( const Info &r, const Info &s ) {
Info rt;
memset ( &rt, 0x3f, sizeof ( rt ) );
for ( int i = ; i <= ; i ++ )
for ( int j = ; j <= ; j ++ )
for ( int k = i; k <= j; k ++ ) {
rt.dp[i][j] = min ( rt.dp[i][j], r.dp[i][k] + s.dp[k][j] );
}
return rt;
} struct node {
Info info;
node *ls, *rs;
} pool[N*], *tail = pool, *root; int a[N];
char s[N];
node *build ( int l, int r ) {
node *nd = ++tail;
if ( l == r ) {
nd -> info.init ( a[l] );
return nd;
}
int mid = ( l + r ) >> ;
nd -> ls = build ( l, mid );
nd -> rs = build ( mid + , r );
nd -> info = nd -> ls -> info + nd -> rs -> info;
return nd;
} Info query ( node *nd, int l, int r, int L, int R ) {
if ( l >= L && r <= R ) return nd -> info;
int mid = ( l + r ) >> ;
if ( R <= mid ) return query ( nd -> ls, l, mid, L, R );
else if ( L > mid ) return query ( nd -> rs, mid + , r, L, R );
else return query ( nd -> ls, l, mid, L, R ) + query ( nd -> rs, mid + , r, L, R );
} int n, q;
int query ( int l, int r ) {
Info info = query ( root, , n, l, r );
return info.dp[][] == oo ? - : info.dp[][];
} int main ( ) {
freopen ( "year.in", "r", stdin );
freopen ( "year.out", "w", stdout );
scanf ( "%s", s + );
scanf ( "%d", &q );
n = strlen ( s + );
for ( int i = ; i <= n; i ++ )
a[i] = s[i] - '';
root = build ( , n );
while ( q -- ) {
int l, r;
scanf ( "%d%d", &l, &r );
printf ( "%d\n", query ( l, r ) );
}
return ;
}

【8.26校内测试】【重构树求直径】【BFS模拟】【线段树维护DP】的更多相关文章

  1. [CSP-S模拟测试]:椎(线段树维护区间最值和单调栈)

    题目描述 虽不能至,心向往之. $Treap=Tree+Heap$ 椎$=$树$+$堆 小$\pi$学习了计算机科学中的数据结构$Treap$. 小$\pi$知道$Treap$指的是一种树. 小$\p ...

  2. 7.18 NOI模拟赛 因懒无名 线段树分治 线段树维护直径

    LINK:因懒无名 20分显然有\(n\cdot q\)的暴力. 还有20分 每次只询问一种颜色的直径不过带修改. 容易想到利用线段树维护直径就可以解决了. 当然也可以进行线段树分治 每种颜色存一下直 ...

  3. bzoj 3551 kruskal重构树dfs序上的主席树

    强制在线 kruskal重构树,每两点间的最大边权即为其lca的点权. 倍增找,dfs序对应区间搞主席树 #include<cstdio> #include<cstring> ...

  4. 【8.23校内测试】【贪心】【线段树优化DP】

    $m$的数据范围看起来非常有问题??仔细多列几个例子可以发现,在$m<=5$的时候,只要找到有两行状态按位$&$起来等于$0$,就是可行方案,如果没有就不行. #include<i ...

  5. 倍增/线段树维护树的直径 hdu5993/2016icpc青岛L

    题意: 给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径 点10W,询问10W,询问相互独立 Solution: 考虑线段树/倍增维护树的直径 考虑一个点集的区间 [l, r] 而我们知道了有 ...

  6. Snow的追寻--线段树维护树的直径

    Snow终于得知母亲是谁,他现在要出发寻找母亲.王国中的路由于某种特殊原因,成为了一棵有n个节点的根节点为1的树,但由于"Birds are everywhere.",他得到了种种 ...

  7. HDU1255 覆盖的面积 —— 求矩形交面积 线段树 + 扫描线 + 离散化

    题目链接:https://vjudge.net/problem/HDU-1255 给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积. Input输入数据的第一行是一个正整数T(1<= ...

  8. 【10.26校内测试】【状压?DP】【最小生成树?搜索?】

    Solution 据说正解DP30行??? 然后写了100行的状压DP?? 疯狂特判,一算极限时间复杂度过不了aaa!! 然而还是过了....QAQ 所以我定的状态是待转移的位置的前三位,用6位二进制 ...

  9. 【11.8校内测试】【倒计时2天】【状压DP】【随机化?/暴力小模拟】

    Solution 数据范围疯狂暗示状压,可是一开始发现状态特别难受. 将每一层的奇偶性状压,预处理所有状态的奇偶性.每一层的输入代表的其实可以是下一层某个点可以被从这一层哪些点转移到. 所以枚举每个状 ...

随机推荐

  1. 2017中国大学生程序设计竞赛 - 网络选拔赛 1003 HDU 6152 Friend-Graph (模拟)

    题目链接 Problem Description It is well known that small groups are not conducive of the development of ...

  2. JodaTime报时区异常错误

    在将爬下来的网页解析需要的字段批量入口的时候(逻辑类似下面): @Test public void test_001(){ String TIME = "1990-04-15"; ...

  3. bzoj 1072 状压DP

    我们用w[i][j]来表示,i是一个二进制表示我们选取了s中的某些位,j表示这些位%d为j,w[i][j]则表示这样情况下的方案数,那么我们可以得到转移.w[i|(1<<k)][(j*10 ...

  4. docker ubuntu容器更换阿里源(转)

    问题:使用docker 利用下载的ubuntu镜像启动容器时,使用的源下载更新软件的速度较慢. 解决这个问题的方法是跟新ubuntu容器的源 示例:以ubuntu为基础镜像 启动一个名称为 test0 ...

  5. struts2的action类详解

    Action类的书写方式 方式1

  6. 环境变量配错了 command not found

    一般就是忘记在PATH 前面加$ 1.可以用whereis或者which命令查看一下有没有这个命令 具体执行which lswhereis ls 2.系统环境变量导致的问题解决方案: exportPA ...

  7. flask基础之jijia2模板语言进阶(三)

    前言 前面学习了jijia2模板语言的一些基础知识,接下来继续深挖jijia2语言的用法. 系列文章 flask基础之安装和使用入门(一) flask基础之jijia2模板使用基础(二) 控制语句 和 ...

  8. 根据名字杀死进程Killall

    Killall命令可以用来给一个特定的进程发送一个信号.这个信号默认情况下是SIGTERM,但也可以由killall命令使用参数来指定其它信号.现在让我们通过一些实际的例子来看看这个命令的实际用法. ...

  9. c#中值类型和引用类型的区别

    1.    值类型的数据存储在内存的栈中:引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址. 2.     值类型存取速度快,引用类型存取速度慢. 3.     值类型表示实际数据,引 ...

  10. opencv3.10加入OPENCV_contrib模块

    在VS2015+opencv3.1进行算法研究时,遇到了一些模块在官网下载的里面是没有的,需要自己进行编译,参考以下链接 http://blog.csdn.net/liu798675179/artic ...