暑假第二十五测

Posted edsheeran

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假第二十五测相关的知识,希望对你有一定的参考价值。

以后WA了T了看数组;

暑假四次数组下标超界,多次数组开小,暂时没有访问到负下标

 

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

题解;

第一题;这道题可以转换为颜色相同的点缩成一个点,每次可以将两个点合并成同一点,问最少几次将所有点合并成一个点;

开始想到并查集+贪心合并度数最多的并查集,但这样是有问题的,比如度数一样时,选择的先后顺序是有影响的;

正解:缩点+找直径,如果是一条黑白相间的链,就是点数/2, 而树上任何一条直径都会有一个点经过直径,我们从交点开始往外延伸,发现最长延伸就是直径本身;

思想:从特殊到一般

技术分享图片
#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
", 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
", 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
", dep[res]/ 2);
    }
    return 0;
}
View Code

 

第二题:模拟,我们发现很多路径是重复的,所以要想办法避免;又发现格子最多有150^2个,所以我们只需要记录格子是否被访问;每次从一个格子暴力沿八个方向走,如果到一个格子也有相同的方向,如果该格子走的远,就停下来,让这个格子完成任务,如果该格子走的不如当前远,那么他的延伸就是废的,去掉;

每个格子最多朝8个方向时被重复,就是O(300*300*8);

这道题我数组开的N,循环的时候也循环到N, 开了O2全WA,都学这么久了,怎么还在犯这种低级错误!!!

而且上次考试又是少写了一个等号全WA!!!该好好练练码力了;

技术分享图片
#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
", 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
",i,j);
    printf("%d
", ans);
    //int tt=clock();
    //cout<<tt-cc;
}
View Code

 

第三题: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]);

但不可能每次询问都做一次,所以用线段树做,转移相当于线段树合并;代码可以学习一下

技术分享图片
#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
",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
", Query(l, r));
    }
    
}
View Code

 

以上是关于暑假第二十五测的主要内容,如果未能解决你的问题,请参考以下文章

平时二十五测

-考研第二十五周总结-

-考研第二十五周总结-

暑假第五测

第二十五篇抽屉效果的核心代码

小刘同学的第二十五篇博文