暑假第二十五测
以后WA了T了看数组;
暑假四次数组下标超界,多次数组开小,暂时没有访问到负下标
题解;
第一题;这道题可以转换为颜色相同的点缩成一个点,每次可以将两个点合并成同一点,问最少几次将所有点合并成一个点;
开始想到并查集+贪心合并度数最多的并查集,但这样是有问题的,比如度数一样时,选择的先后顺序是有影响的;
正解:缩点+找直径,如果是一条黑白相间的链,就是点数/2, 而树上任何一条直径都会有一个点经过直径,我们从交点开始往外延伸,发现最长延伸就是直径本身;
思想:从特殊到一般
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int M = 1e5 + 10; #define ex(i, u) for(int i = h[u]; i; i = G[i].nxt) int h[M], col[M], tot, fa[M], rec[M], vec[M], dep[M]; struct edge{int nxt, v;}G[M * 2]; void add(int u, int v){ G[++tot].nxt = h[u]; h[u] = tot; G[tot].v = v; } int find(int u){ if(u == fa[u])return u; return fa[u] = find(fa[u]); } void uni(int u, int v){ fa[find(u)] = find(v); } int read(){ int x = 0,f = 1; char c = getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*=f; } int rt, res; void dfs(int u, int f){ //fprintf(stderr , "%d %d\n", u, f); dep[u] = dep[f] + 1; ex(i, u){ int v = G[i].v; if(v == f)continue; dfs(v, u); } if(dep[u] > dep[res])res = u; } int main(){ freopen("color.in","r",stdin); freopen("color.out","w",stdout); int T; scanf("%d", &T); while(T--){ int n = read(); for(int i = 1; i <= n; i++) col[i] = read(), h[i] = 0, fa[i] = i; tot = 0; int u, v, p = 0; for(int i = 1; i < n; i++){ u = read(), v = read(); if(col[u] == col[v])uni(u, v); else vec[++p] = u, rec[p] = v; } for(int i = 1; i <= p; i++){ int x = find(rec[i]), y = find(vec[i]); add(x, y), add(y, x); //fprintf(stderr, "%d %d\n", y, x); } for(int i = 1; i <= n; i++) if(i == find(i)){ res = rt = 0; memset(dep, 0, sizeof(dep)); dfs(i, 0); memset(dep, 0, sizeof(dep)); rt = res; dfs(rt, res = 0); break; } printf("%d\n", dep[res]/ 2); } return 0; }
第二题:模拟,我们发现很多路径是重复的,所以要想办法避免;又发现格子最多有150^2个,所以我们只需要记录格子是否被访问;每次从一个格子暴力沿八个方向走,如果到一个格子也有相同的方向,如果该格子走的远,就停下来,让这个格子完成任务,如果该格子走的不如当前远,那么他的延伸就是废的,去掉;
每个格子最多朝8个方向时被重复,就是O(300*300*8);
这道题我数组开的N,循环的时候也循环到N, 开了O2全WA,都学这么久了,怎么还在犯这种低级错误!!!
而且上次考试又是少写了一个等号全WA!!!该好好练练码力了;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int N = 306; int zl[10][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}}; int go[10][2] = {{0, 0}, {2, 8}, {1, 3}, {2, 4}, {3, 5}, {4, 6}, {5, 7}, {6, 8}, {1, 7}}; int n; int mp[N+11][N+11][10], a[50]; bool vis[N+11][N+11], done[N+11][N+11]; struct node{int x,y,dep,fx;}; void bfs(){ queue <node> q; q.push((node){152, 152, 1, 1}); mp[152][152][1] = 1; while(!q.empty()){ node u = q.front(); q.pop(); done[u.x][u.y] = 1; int i = u.fx; int x = u.x + zl[i][0] * a[u.dep], y = u.y + zl[i][1] * a[u.dep]; if(a[mp[x][y][go[i][0]]] < a[u.dep + 1]){ mp[x][y][go[i][0]] = u.dep + 1;q.push((node){x, y, u.dep + 1, go[i][0]}); } if(a[mp[x][y][go[i][1]]] < a[u.dep + 1]){ mp[x][y][go[i][1]] = u.dep + 1;q.push((node){x, y, u.dep + 1, go[i][1]}); } } } void work(){ for(int i = 1; i <= N; i++) for(int j = 1; j <= N; j++){ if(!done[i][j])continue; for(int k = 1; k <= 8; k++){ if(mp[i][j][k]){ //printf("%d %d %d %d\n", i, j, k, mp[i][j][k]); int x = i, y = j; for(int z = 1; z <= a[mp[i][j][k]]; z++){ x += zl[k][0], y += zl[k][1]; if(a[mp[x][y][k]] > a[mp[i][j][k]] - z)break; else mp[x][y][k] = 0; vis[x][y] = 1; } vis[i][j] = 1; } } } } int main(){ freopen("grow.in","r",stdin); freopen("grow.out","w",stdout); //int cc=clock(); int ans = 0; scanf("%d", &n); for(int i = 1; i <= n; i++)scanf("%d", &a[i]); a[1]--; bfs(); work(); for(int i = 1; i <= N; i++) for(int j = 1; j <= N; j++)if(vis[i][j])ans++;//,printf("%d %d\n",i,j); printf("%d\n", ans); //int tt=clock(); //cout<<tt-cc; }
第三题:dp,定义状态 :
0 1 2 3 4
# 2 20 201 2017
dp[s][t]表示从状态s转移到状态t的最少删除字符,我们枚举中间状态r, dp[s][t] = min(dp[s][r] + dp[r][t]);
但不可能每次询问都做一次,所以用线段树做,转移相当于线段树合并;代码可以学习一下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int inf = 1e8, M = 1e5 + 10; int n; char s[M]; struct Info{//没有放在Node里是因为重载运算符不能返回一个指针 int tr[5][5]; // 0 1 2 3 4 // $ 2 20 201 2017 void init(int v){ memset(tr, 0x3f, sizeof(tr)); if(v == 3 || v == 4 || v == 5 || v == 8 || v == 9){ for(int i = 0; i < 5; i ++)tr[i][i] = 0; } if(v == 2){ tr[0][0] = 1; tr[0][1] = 0; tr[1][1] = tr[2][2] = tr[3][3] = tr[4][4] = 0; } if(v == 0){ tr[1][2] = 0; tr[1][1] = 1; tr[0][0] = tr[2][2] = tr[3][3] = tr[4][4] = 0; } if(v == 1){ tr[2][3] = 0; tr[2][2] = 1; tr[0][0] = tr[3][3] = tr[4][4] = tr[1][1] = 0; } if(v == 7){ tr[3][3] = 1; tr[3][4] = 0; tr[1][1] = tr[4][4] = tr[2][2] = tr[0][0] = 0; } if(v == 6){ tr[3][3] = tr[4][4] = 1; tr[0][0] = tr[1][1] = tr[2][2] = 0; } } }; struct Node{ Info info; Node *ls, *rs; }pool[(M << 2)], *tail = pool, *root; Info operator + (const Info &a, const Info &b){//重载运算符写法 Info rt; memset(&rt, 0x3f, sizeof(rt));//取地址 for(int i = 0; i < 5; i++) for(int j = 0; j < 5; 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]); /*for(int i = 0;i<5;i++)for(int j=0; j<5;j++) printf("%d %d %d\n",i,j,zero->tr[i][j]);puts("");*/ return rt; } Node * build(int lf = 1, int rg = n){ Node *nd = ++tail; if(lf == rg)nd->info.init(s[lf - 1] - '0'); else { int mid = (lf + rg) >> 1; nd->ls = build(lf, mid); nd->rs = build(mid + 1, rg); nd->info = nd->ls->info + nd->rs->info; } return nd; } Info query(int L, int R, Node * nd = root, int lf = 1, int rg = n){ if(lf >= L && rg <= R)return nd->info; int mid = (lf + rg) >> 1; if(L > mid)return query(L, R, nd->rs, mid + 1, rg); else if(R <= mid)return query(L, R, nd->ls, lf, mid); else return query(L, R, nd->ls, lf, mid) + query(L, R, nd->rs, mid + 1, rg); } int Query(int l, int r){ Info ans = query(l, r); return ans.tr[0][4] < inf ? ans.tr[0][4] : -1; } int main(){ freopen("year.in","r",stdin); freopen("year.out","w",stdout); scanf("%s", s); n = strlen(s); root = build(); int q, l, r; scanf("%d", &q); while(q--){ scanf("%d%d", &l, &r); printf("%d\n", Query(l, r)); } }