我真的服了 我考试的时候这道题题都是读错了的 交了个挖挖机结果还狗了20分..
这道题是一道找规律的题 看完题很显然能够发现我们可以将相同颜色的连通块缩点
因为同一个联通块的可以一次操作全部变成另外一种颜色 所以就缩点就好了..
对于缩点后的一条链
每次我们可以将一个点变色 那么和他相邻的点就和他颜色一样 然后就再次缩点 所以每次链的长度都可以 -2
所以说最后缩点的次数就是 点的个数 / 2 (下取整)
但是这是对于一条链 那么对于一棵树而言呢?
其实是一样的 因为每次搞这个操作 我们都可以把它周围的点都缩起来 那么对于多条链的情况
每次就选择两链的交点进行这个操作 经过这个点的所有链长度均 - 2 所以操作次数就是最长链 也就是直径的点数 / 2(下取整)
代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n,c[N],node,T,tot,head[N],nex[2 * N],tov[2 * N]; int id[N],fa[N],dis[N],ma,ev[N],eu[N],cnt; bool vis[N]; queue<int>Q; int find_fa(int u) { return u == fa[u] ? u : find_fa(fa[u]); } void merge(int u,int v) { int fa1 = find_fa(u); int fa2 = find_fa(v); if(fa1 == fa2) return ; fa[fa2] = fa1; } void add(int u,int v) { tot ++; nex[tot] = head[u]; tov[tot] = v; head[u] = tot; } void bfs(int st) { memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[st] = 0; vis[st] = true; node = st,ma = 0; Q.push(st); while(! Q.empty( )) { int u = Q.front( ); Q.pop( ); for(int i = head[u];i;i = nex[i]) { int v = tov[i]; if(vis[v]) continue; vis[v] = true; dis[v] = dis[u] + 1; if(dis[v] > ma) { ma = dis[v]; node = v; } Q.push(v); } } return ; } void init( ) { memset(head,0,sizeof(head)); tot = 0; for(int i = 1;i <= n;i ++) fa[i] = i; } int main( ) { freopen("color.in","r",stdin); freopen("color.out","w",stdout); scanf("%d",& T); while(T --) { scanf("%d",& n); init( ); for(int i = 1;i <= n;i ++) scanf("%d",& c[i]); for(int i = 1;i < n;i ++) { scanf("%d%d",& eu[i],& ev[i]); if(c[eu[i]] == c[ev[i]]) { merge(eu[i],ev[i]); } } for(int i = 1;i < n;i ++) { int f1 = find_fa(eu[i]),f2 = find_fa(ev[i]); if(f1 != f2) { if(! id[f1]) { id[f1] = ++ cnt; } if(! id[f2]) { id[f2] = ++ cnt; } add(id[f1],id[f2]); add(id[f2],id[f1]); } } bfs(1); bfs(node); printf("%d ",(dis[node] + 1)/2); } }
考试的时候打得垃圾暴力狗了30
这道题是一道bfs模拟 题 因为t很小 所以可以发现从初始点(175,175)出发的点范围肯定在(400,400)以内
每一个状态可以拓展出的状态很多 所以会出现很多重复的状态 拓展过的状态就不用重新再计算了
所以就开一个vis数组表示这个情况是否被拓展过 然后就很简单了
代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int dp[400][400],t[N],h[2] = {-1,1},n,ans = 0; bool vis[400][400][40][8]; struct node { int x,y,s,d; node(){} node( int x, int y, int s, int d ):x(x),y(y),s(s),d(d){} }stat; int dx[] = {0,-1,-1,-1,0,1,1,1}; int dy[] = {1,1,0,-1,-1,-1,0,1}; queue<node>Q; void bfs( ) { while(! Q.empty()) { node u = Q.front( ); Q.pop( ); int xx = u.x,yy = u.y,dep = u.s; if(dep == n) continue; for(int i = 0;i <= 1;i ++) { int dd = ((u.d + h[i]) % 8 + 8) % 8; for(int j = 1;j <= t[dep + 1];j ++) { xx += dx[dd]; yy += dy[dd]; dp[xx][yy] = 1; } if(! vis[xx][yy][dep + 1][dd]) { vis[xx][yy][dep + 1][dd] = true; Q.push(node(xx,yy,dep + 1,dd)); } xx = u.x,yy = u.y; } } for(int i = 0;i < 400;i ++) for(int j = 0;j < 400;j ++) ans += dp[i][j]; } int main( ) { freopen("grow.in","r",stdin); freopen("grow.out","w",stdout); scanf("%d",& n); for(int i = 1;i <= n;i ++) scanf("%d",& t[i]); for(int i = 1;i <= t[1];i ++) { dp[175][174 + i] = true; } vis[175][174 + t[1]][1][0] = true; Q.push(node(175,174 + t[1],1,0)); bfs( ); printf("%d",ans); }
这道题可以转化为类似于状态合并的问题 共有5个状态 空集 2 20 201 2017
tr[ i ][ j ]表示从 i 号状态转移到 j 号状态 最少需要删掉多少字符
那么类似于Floyd 对于两个区间 a b及他的左右两个区间
tr[ i ][ j ] = min(tr[ i ][ k ] + tr[ k ][ j ]) 总的状态可以由他的两个连续子状态转移过来
但是每次都去暴力搞就很糟糕 所以考虑用线段树维护就可以了
代码
#include <bits/stdc++.h> #define oo 0x3f using namespace std; const int N = 2e5 + 5; int n,a[N],q; char s[N]; struct Info { int tr[5][5]; void init(int cur) { memset(tr,0x3f,sizeof(tr)); if(cur == 3 || cur == 4 || cur == 5 || cur == 8 || cur == 9) { for(int i = 0;i <= 4;i ++) tr[i][i] = 0; } if(cur == 2) { tr[0][0] = 1; tr[0][1] = 0; tr[1][1] = tr[2][2] = tr[3][3] = tr[4][4] = 0; } else if(cur == 0) { tr[1][2] = 0; tr[1][1] = 1; tr[0][0] = tr[2][2] = tr[3][3] = tr[4][4] = 0; } else if(cur == 1) { tr[2][3] = 0; tr[2][2] = 1; tr[0][0] = tr[1][1] = tr[3][3] = tr[4][4] = 0; } else if(cur == 6) { tr[3][3] = 1; tr[4][4] = 1; tr[0][0] = tr[1][1] = tr[2][2] = 0; } else if(cur == 7) { tr[3][4] = 0; tr[3][3] = 1; tr[0][0] = tr[1][1] = tr[2][2] = tr[4][4] = 0; } } }; Info operator + (const Info & a,const Info & b) { Info rt; memset(& rt,0x3f,sizeof(rt)); for(int i = 0;i <= 4;i ++) for(int j = 0;j <= 4;j ++) for(int k = i;k <= j;k ++) rt.tr[i][j] = min(rt.tr[i][j],a.tr[i][k] + b.tr[k][j]); return rt; } struct node { Info info; node *ls,*rs; }pool[4 * N],*tail = pool,*root; node *build(int l,int r) { node *nd = ++ tail; if(l == r) { nd -> info.init(a[l]); return nd; } int mid = (l + r) >> 1; nd -> ls = build(l,mid); nd -> rs = build(mid + 1,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) >> 1; if(R <= mid) return query(nd -> ls,l,mid,L,R); else if(L > mid) return query(nd -> rs,mid + 1,r,L,R); else return query(nd -> ls,l,mid,L,R) + query(nd -> rs,mid + 1,r,L,R); } int main( ) { freopen("year.in","r",stdin); freopen("year.out","w",stdout); scanf("%s",s + 1); int len = strlen(s + 1); for(int i = 1;i <= len;i ++) a[i] = s[i] - '0'; root = build(1,len); scanf("%d",& q); while(q --) { int l,r; scanf("%d%d",& l,& r); Info nd = query(root,1,len,l,r); if(nd.tr[0][4] == 1061109567) printf("-1 "); else printf("%d ",nd.tr[0][4]); } }