2016CCPC东北地区大学生程序设计竞赛 解题报告(部分)

A - Minimum’s Revenge  HDU - 5922  水题

思路:大水题,分析一下就能发现只要所有点都和1连就好了,然后写个等差数列求和公式就过了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(int argc, char const *argv[])
{
    int t;
    scanf("%d", &t);
    int k = 1;
    while(t--){
        LL n;
        scanf("%I64d", &n);
        printf("Case #%d: %I64d\n", k++, (2LL + n) * (n - 1LL) / 2LL);
    }
    return 0;
}

B - Prediction  HDU - 5923  并查集求连通分量数量

思路:说实话这题是真的难读(可能是我的乡村英语太菜了)。。。最后看了题解才知道这题什么意思,据各位大佬说数据很水,非常暴力的写法都能过,建立m个并查集 ,每次询问直接常规查询就好了,然后就是一个并查集求连通分量的数量了。。。。

细节请看代码,顺便说一下可能会有人拿vector记录当选某个边时所能得到的所有边,会MLE。。。别问我为什么。。。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e4 + 50;
int n, m;
struct Edge
{
    int u, to, next;
} tree[maxn], edge[maxn];

int k, head[maxn];

void add(int a, int b){
    tree[k].to = b;
    tree[k].next = head[a];
    head[a] = k++;
}

int par[maxn][600];
int Find(int id, int x){
    if(par[id][x] == x){
        return x;
    }
    return par[id][x] = Find(id, par[id][x]);
}


void init(){
    k = 0;
    for(int i = 1; i <= m; i++){
        head[i] = -1;
    }
    for(int i = 1; i <= n; i++){
        par[0][i] = i;
    }
}



void unit(int id, int x, int y){
    x = Find(id, x);
    y = Find(id, y);
    if(x != y){
        par[id][x] = par[id][y];
    }
}

void mcpy(int pre, int u){
    for(int i = 1; i <= n; i++){
        par[u][i] = par[pre][i];
    }
}

void dfs(int u, int pre){
    mcpy(pre, u); // 因为字节点选了,他的所有祖先都要选,所以要记录之前的情况
    unit(u, edge[u].u, edge[u].to);
    for(int i = head[u]; i != -1; i = tree[i].next){
        int to = tree[i].to;
        dfs(to, u);
    }
}
int main() {
    int t;
    scanf("%d", &t);
    int ca = 1;
    while(t--){
        printf("Case #%d:\n", ca++);
        scanf("%d%d", &n, &m);
        init();
        for(int i = 1; i < m; i++){
            int x;
            scanf("%d", &x);
            add(x, i + 1);
        }
        for(int i = 1; i <= m; i++){
            scanf("%d%d", &edge[i].u, &edge[i].to);
        }
        dfs(1, 0);
        int q;
        scanf("%d", &q);
        while(q--){
            int num;
            scanf("%d", &num);
            mcpy(0, m + 1);
            while(num--){
                int x;
                scanf("%d", &x);
                for(int i = 1; i <= n; i++){ 
                    int p1 = Find(m + 1, i); // 判断点 i 在m + 1和 x 里的祖先,若不同, 则要把p2和p1连上
                    int p2 = Find(x, i);
                    unit(m + 1, p1, p2);
                }
            }

            int ans = 0;
            for(int i = 1; i <= n; i++){
                if(par[m + 1][i] == i){
                    ans++;
                }
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}

 

C - Mr. Frog’s Problem HDU - 5924  思维题

思路:证明一下发现就两种情况。。 证明过程就不说了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e4 + 50;
int n, m;

int main() {
    int t;
    scanf("%d", &t);
    int ca = 1;
    while(t--){
        printf("Case #%d:\n", ca++);
        LL a, b;
        scanf("%I64d%I64d", &a, &b);
        if(a == b){
            printf("1\n");
            printf("%I64d %I64d\n", a, b);
        } else{
            printf("2\n");
            printf("%I64d %I64d\n", a, b);
            printf("%I64d %I64d\n", b, a);
        }
    }
    return 0;
}

D - Coconuts HDU - 5925  离散化 + 求联通块

思路:每次获取一个坐标x或y时, 我们把 x 和 x - 1 或 y 和 y - 1加入lshx 或 lshy数组, 最后在把0 和 n(R)加入lshx数组, 0 和 m(C)加入lshy数组, 离散化两个数组, cntx为不同的 x的数量, cnty同理, 这些x, y 所对应的线把矩阵分割成了(cntx - 1) * (cnty - 1)个小矩阵,求出每个小矩阵里格子的数量,在用一个权值为该数量的1 x 1的矩阵代替这个小矩阵,然后原矩阵就变成了每个格子都有权值的 (cntx - 1) * (cnty - 1)的矩阵了,按照这种分法, 每个坏点都是一个独立的1 x 1矩阵, 并把坏点的矩阵权值设为0,代表不可走,然后就是dfs求联通块个数,联通块中格子的权值相加就是每个连通快点的个数了, 因为这题会爆int, 所以要开long long,可我写的时候忘了。。。一个个改过来有太麻烦。。。就直接一键替换了,所以我的变量类型都是long long。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 2e4 + 50;
LL n, m;
struct date
{
    LL x, y;
} a[maxn];

LL lshx[maxn], lshy[maxn];
LL mp[500][500];

LL sum = 0;
LL vis[500][500];
LL cntx = 0, cnty = 0;
LL tx[] = {0, -1, 0, 1};
LL ty[] = {1, 0, -1, 0};
void dfs(LL x, LL y){
    sum += mp[x][y];
    vis[x][y] = 1;
    for(LL i = 0; i < 4; i++){
        LL nx = x + tx[i];
        LL ny = y + ty[i];
        if(nx >= 1 && nx < cntx && ny >= 1 && ny < cnty && mp[nx][ny] && !vis[nx][ny]){
            dfs(nx, ny);
        }
    }
}

vector<LL> vec;
int main(int argc, char const *argv[])
{
    LL t;
    scanf("%I64d", &t);
    LL ca = 1;
    while(t--){
        printf("Case #%I64d:\n", ca++);
        vec.clear();
        cntx = cnty = 0;
        memset(mp, 0, sizeof(mp));
        memset(vis, 0, sizeof(vis));
        scanf("%I64d%I64d", &n, &m);
        LL q;
        scanf("%I64d", &q);
        for(LL i = 0; i < q; i++){
            LL x, y;
            scanf("%I64d%I64d", &x, &y);
            a[i].x = x, a[i].y = y;
            lshx[++cntx] = x - 1;
            lshx[++cntx] = x;
            lshy[++cnty] = y - 1;
            lshy[++cnty] = y;
        }
        lshx[++cntx] = 0;
        lshx[++cntx] = n;
        lshy[++cnty] = 0;
        lshy[++cnty] = m;

        sort(lshx + 1, lshx + cntx + 1);
        sort(lshy + 1, lshy + cnty + 1);
        cntx = unique(lshx + 1, lshx + cntx + 1) - lshx - 1;
        cnty = unique(lshy + 1, lshy + cnty + 1) - lshy - 1;
        for(LL i = 2; i <= cntx; i++){
            for(LL j = 2; j <= cnty; j++){
                mp[i - 1][j - 1] = (lshx[i] - lshx[i - 1]) * (lshy[j] - lshy[j - 1]);
            }
        }

        for(LL i = 0; i < q; i++){
            a[i].x = lower_bound(lshx + 1, lshx + cntx + 1, a[i].x) - lshx;
            a[i].y = lower_bound(lshy + 1, lshy + cnty + 1, a[i].y) - lshy;
            mp[a[i].x - 1][a[i].y - 1] = 0;
        }

        LL ans = 0;
        for(LL i = 1; i < cntx; i++){
            for(LL j = 1; j < cnty; j++){
                if(mp[i][j] && !vis[i][j]){
                    sum = 0;
                    ans++;
                    dfs(i, j);
                    vec.push_back(sum);
                }
            }
        }

        printf("%I64d\n", ans);
        sort(vec.begin(), vec.end());
        LL len = vec.size();
        for(LL i = 0; i < len; i++){
            printf("%I64d", vec[i]);
            if(i != len - 1){
                printf(" ");
            }
        }
        printf("\n");
    }
    return 0;
}

 

E - Mr. Frog’s Game  HDU - 5926 暴力艹就好了

思路: 因为给的状态时最初的,所以每个点都是为消去的,我们只用判断有没有相邻的并且相同的点和四条边上有没有相同的点就好了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 50;
int n, m;

int mp[maxn][maxn];
int tx[] = {0, -1, 0, 1};
int ty[] = {1, 0, -1, 0};
int main(int argc, char const *argv[])
{
    int t;
    scanf("%d", &t);
    int cnt = 1;
    
    while(t--){
        memset(mp, -1, sizeof(mp));
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                scanf("%d", &mp[i][j]);
            }
        }

        int flag = 0;

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                for(int k = 0; k < 4; k++){
                    int nx = i + tx[k];
                    int ny = j + ty[k];
                    if(nx >= 1 && nx <= n && ny >= 1 && ny <= m){
                        if(mp[i][j] == mp[nx][ny]){
                            flag = 1;
                        }
                    }
                }
            }
        }

        for(int i = 1; i <= m; i++){
            for(int j = i + 1; j <= m; j++){
                if(mp[1][i] == mp[1][j]){
                    flag = 1;
                   
                }
                if(mp[n][i] == mp[n][j]){
                     
                    flag = 1;
                }
            }
        }

        for(int i = 1; i <= n; i++){
            for(int j = i + 1; j <= n; j++){
                if(mp[i][1] == mp[j][1]){
                    flag = 1;
                }
                if(mp[i][m] == mp[j][m]){
                    flag = 1;
                }
            }
        }

        printf("Case #%d: ", cnt++);
        if(flag){
            printf("Yes\n");
        } else{
            printf("No\n");
        }
    }
    return 0;
}

 

F - Auxiliary Set HDU - 5927 思维题

思路:由题可知,若一个点为重要点,要么它本身是重要点,要么至少有两个子树上含有重要点,所以我们可以从下往上推,每次遍历所有点是不可能的,我们只用考虑它给的点就好了,细节看代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;

const int maxn = 1e5 + 60;
int n, q;
struct Edge
{
	int to, next;
} edge[maxn * 2];

int k, head[maxn];
void add(int a, int b){
	edge[k].to = b;
	edge[k].next = head[a];
	head[a] = k++;
}
int dep[maxn], son[maxn], par[maxn];
void dfs(int u, int pre, int d){
	dep[u] = d;
	par[u] = pre;
	for(int i = head[u]; i != -1; i = edge[i].next){
		int to = edge[i].to;
		if(to == pre){
			continue;
		}
		dfs(to, u, d + 1);
		son[u]++;
	}
}

int b[maxn];

bool cmp(int x, int y){
	return dep[x] > dep[y];
}

int res[maxn];
int main()
{
    int t;
    scanf("%d", &t);
    int ca = 1;
    while(t--){
    	printf("Case #%d:\n", ca++);
    	scanf("%d%d", &n, &q);
    	k = 0;
    	for(int i = 1; i <= n; i++){
    		son[i] = 0;
    		head[i] = -1;
    	}
    	for(int i = 1; i < n; i++){
    		int a, b;
    		scanf("%d%d", &a, &b);
    		add(a, b);
    		add(b, a);
    	}
    	dfs(1, 0, 1);
    	while(q--){
    		int num;
    		scanf("%d", &num);
    		int ans = n - num;
    		for(int i = 1; i <= num; i++){
    			scanf("%d", &b[i]);
    		}
    		sort(b + 1, b + num + 1, cmp);
    		for(int i = 1; i <= num; i++){ // 注意这里没必要初始化每个点
    			res[b[i]] = son[b[i]];
    		}
    		for(int i = 1; i <= num; i++){
    			int u = b[i];
    			if(res[u] == 0){ // 如果点u不是重要点,说明以它为根的这颗子树上没有重要节点,那么u的父亲的重要节点数要减1
    				res[par[u]]--;
    			}
    			if(res[u] >= 2){
    				ans++;
    			}
    		}

    		printf("%d\n", ans);
    	}
    }
    return 0;
}

 

G - Birthday Gift HDU - 5928 极角排序 + DP

我不会。。。

H - Basic Data Structure HDU - 5929  模拟

思路:每次找尾部开始连续1的个数就好了,还要考虑最后面的0的位置,若最后一个0的位置不是在头部,则连续1的个数+ 1,因为0怎么运算都是1,连续1的个数为奇数则结果为1, 偶数则为0,连续1的个数我们用数组模拟队列来维护,0的位置用deque来维护

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e6 + 50;
int n, m;

int s[maxn];
int dp[maxn];
deque<int> que; 
int main(int argc, char const *argv[])
{
    int t;
    scanf("%d", &t);
    int ca = 1;
    while(t--){
        while(que.size()){
            que.pop_back();
        }
        printf("Case #%d:\n", ca++);
        int top = 200000 + 50, tail = 200000 + 50 - 1;
        int flag = 1; //用来判断是数组的前面作为头部还是后面
        int sum = 0;
        scanf("%d", &n);
        while(n--){
            char ss[20];
            scanf("%s", ss);
            if(ss[2] == 'S'){
                int x;
                scanf("%d", &x);
                if(flag){
                    ++tail;
                    s[tail] = x;
                } else{
                    top--;
                    s[top] = x;
                }
                sum += x;
                if(x == 0){
                    if(flag){
                        que.push_back(tail);
                    } else{
                        que.push_front(top);
                    }
                }
            } else if(ss[2] == 'P'){
                if(flag){
                    sum -= s[tail];
                    if(que.size() && que.back() == tail){
                        que.pop_back();
                    }
                    tail--;
                } else{
                    sum -= s[top];
                    if(que.size() && que.front() == top){
                        que.pop_front();
                    }
                    top++;
                }
            } else if(ss[2] == 'E'){
                if(top > tail){
                    printf("Invalid.\n");
                } else{
                    if(top == tail){
                        printf("%d\n", s[top]);
                        continue;
                    }
                    if(sum == tail - top + 1){
                        if(sum % 2 == 1){
                            printf("1\n");
                        } else{
                            printf("0\n");
                        }
                        continue;
                    }
                    if(flag){
                        int st = que.front();
                        int cnt;
                        if(st == top){
                            printf("1\n");
                        } else{
                            if(st == tail){
                                cnt = tail - top;
                            } else{
                                cnt = st - top + 1;
                            }
                            if(cnt % 2 == 0){
                                printf("0\n");
                            } else{
                                printf("1\n");
                            }
                        }
                    } else{
                        int st = que.back();
                        int cnt;
                        if(st == tail){
                            printf("1\n");
                        } else{
                            if(st == top){
                                cnt = tail - top;
                            } else{
                                cnt = tail - st + 1;
                            }
                            if(cnt % 2 == 0){
                                printf("0\n");
                            } else{
                                printf("1\n");
                            }
                        }
                    }
                }
            } else{
                flag ^= 1; // 反转序列
            }
        }
    }
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值