广东工业大学第12届ACM程序设计大赛部分题解

A

#include <cstdio>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
 
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(Arr, x) memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)    for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int INF = 1e9 + 10;
int n, m;
 
int main(){
    int t;  scanf("%d", &t);
    while(t--){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        int num = c / 4;
        int x = num - b;
        int y = num - a / 2;
        printf("%d\n", num - x - y);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1224
    User: Yokile
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:1696 kb
****************************************************************/



B

参考官方题解:

令 d = abs(x1-x2)+abs(y1-y2)首先判断(n+1)/2 >= d,先手可不可以从一个点走到另一个点 :

如果不可以,则先手可以多得 n&1 分(因为劣势者可以选择逃离)

如果可以,考虑 d 的奇偶性:如果 d 为奇数(先手可以先踩到后手覆盖过的点):

如果 n 为奇数,先手可以多得 2 分,否则平。否则(d 为偶数)

:如果 n 为奇数,先手可以多得 1 分,否则后手可以多得 1 分。


#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;
#define LL long long

int main(){
	int t;	scanf("%d", &t);
	while(t--){
		LL n, x1, x2, y1, y2;
		scanf("%lld%lld%lld%lld%lld", &n, &x1, &y1, &x2, &y2);
		LL d = abs(x1 - x2) + abs(y1 - y2);
		LL ans = -1;
		if((n + 1) / 2 >= d){
			if(d % 2 == 0){
				ans = 1;
			}else{
				if(n & 1)	ans = 2;
			}
		}else if(n & 1){
			ans = 1;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

这题还是不难的,- - 、果然还是萌新阿


C

这题也就是简单地递推, 然后乘法原理即可

#include <cstdio>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
 
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(Arr, x) memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)    for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int INF = 1e9 + 10;
const int MOD = 10007;
int n;
LL dp[30];
 
int main(){
    dp[0] = 0, dp[1] = 1, dp[2] = 2, dp[3] = 4;
    for(int i = 4; i <= 20; ++i)
        dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
    int t;  scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        LL tot = 1;
        REP(i, 1, n){
            int x;  scanf("%d", &x);
            tot = (tot * dp[x]) % MOD;
        }
        printf("%lld\n", tot);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1226
    User: Yokile
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:1696 kb
****************************************************************/


D

#include <cstdio>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
 
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(Arr, x) memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)    for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int INF = 1e9 + 10;
int n, m;
 
int main(){
    int t;  scanf("%d", &t);
    while(t--){
        LL a, b, c; scanf("%lld%lld%lld", &a, &b, &c);
        LL sum = a * b;
        sum = sum + (a / 10 / 3) * c;
        printf("%lld\n", sum);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1227
    User: Yokile
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:0 kb
****************************************************************/


E

这题貌似标程除了问题 题目还需要重判,但我觉得我的算法没啥问题

首先要尽可能的缩小瓶子数目,不断除2, 但是当瓶子数目为奇数时就会剩下一瓶,这时候这瓶水就要独立出来,放入一个集合中

最后这个集合进行操作, 首先要明确, 如果你有一瓶x升的水, 这个水一定是2^n, 不会是别的数了, 如果队列中不存在另外一个x,那么就必须补充x瓶水

因为每次都是对最小的进行操作,所以最后求出来肯定是最小的

#include <cstdio>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>

using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(Arr, x) memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)    for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int INF = 1e9 + 10;
int n, m, k;

int main(){
    int t;  scanf("%d", &t);
    while(t--){
        priority_queue<int, vector<int>, greater<int> > Q;
        scanf("%d%d", &n, &k);
        int t = 1;
        while(n){
            if(n & 1)   Q.push(t);
            n /= 2;
            t <<= 1;
        }
        LL minx = 0;
        while((int)Q.size() > k){
            int x = Q.top();    Q.pop();
            int y = Q.top();    Q.pop();
            if(x == y){
                Q.push(x + y);
            }else{
                minx += x;
                Q.push(x + x);
                Q.push(y);
            }
        }
        printf("%lld\n", minx);
    }
    return 0;
}


F

Tarjan离线求出最近公共祖先, 然后求这条路径上所有线段的长度, 然后排一次序, 然后每三个相邻的数判断一下能不能形成三角形

没给时间限制, 就暴力做了, 没想到还过了。。。。

昨天学长教我这题的正确解题姿势, 线段的长度都在1到1e9内, 我们可以这样想,假设三条线段a,b,c,令a<=b=<c, 如果满足a + b > c那么一定是个三角形,那么不满足的极限条件就是a + b = c, 换一种说法,就是在区间1到1e9内选择尽量多的数,使得选择出来的任意三个数都不能组成三角形, 这样推出来的结论就是这些选择出来的数满足菲波那契数列, 然后就可以根据路径的长度进行剪枝了,最终的复杂度还是O(m + q)。学长说好像是2016年大连区域赛的一道题也是这样剪的

#include <cstdio>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
 
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(Arr, x) memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)    for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int INF = 1e9 + 10;
int n, m, top;
int fa[qq], pre[qq], head[qq], plen[qq], anc[qq];
int num[qq];
int l[qq], r[qq];
bool vis[qq];
struct Edge{
    int v, next, w;
}edge[qq * 2];
vector<int> q[qq], f[qq];
void Add(int u, int v, int w){
    edge[top].v = v;
    edge[top].w = w;
    edge[top].next = head[u];
    head[u] = top++;
}
void Init(){
    mst(head, -1);
    mst(vis, false);
    mst(plen, 0);
    mst(anc, 0);
    top = 0;
    for(int i = 0; i <= n; ++i)
        q[i].clear(), f[i].clear();
 
}
int Find(int x){
    return fa[x] == -1 ? x : fa[x] = Find(fa[x]);
}
void Union(int x, int y){
    int b = Find(y);
    if(x != b)  fa[b] = x;
}
void Tarjan(int u, int p, int w){
    fa[u] = pre[u] = -1;
    for(int i = head[u]; i != -1; i = edge[i].next){
        int v = edge[i].v;
        if(v == p || vis[v])    continue;
        Tarjan(v, u, edge[i].w);
        Union(u, v);
    }
    vis[u] = true;
    pre[u] = p;
    plen[u] = w;
    for(int i = 0; i < (int)q[u].size(); ++i)if(vis[q[u][i]]){
        anc[f[u][i]] = Find(q[u][i]);
    }
}
bool check(int c, int b, int a){
    if(a + b > c && a + c > b && b + c > a)    return true;
    return false;
}
 
int main(){
    int t;  scanf("%d", &t);
    while(t--){
        Init();
        scanf("%d", &n);
        REP(i, 1, n){
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            Add(a, b, c);
            Add(b, a, c);
        }
        int m;  scanf("%d", &m);
        REP(i, 0, m){
            int a, b;scanf("%d%d", &a, &b);
            l[i] = a, r[i] = b;
            q[a].pb(b), f[a].pb(i);
            q[b].pb(a), f[b].pb(i);
        }
        Tarjan(1, -1, 0);
    /*for(int i = 0; i < m; ++i){
            printf("%d\n", anc[i]);
        }*/
        REP(i, 0, m){
            int cnt = 0;
            int u = l[i];
            int v = anc[i];
            while(u != v){
                num[cnt++] = plen[u];
                u = pre[u];
            }
            u = r[i];
            while(u != v){
                num[cnt++] = plen[u];
                u = pre[u];
            }
            sort(num, num + cnt);
            bool flag = false;
            for(int j = 2; j < cnt; ++j){
                if(check(num[j], num[j - 1], num[j - 2])){
                    flag = true;
                    break;
                }
            }
            if(flag)    puts("Yes");
            else    puts("No");
        }
    }
    return 0;
}
 
/**************************************************************
    Problem: 1229
    User: Yokile
    Language: C++
    Result: Accepted
    Time:104 ms
    Memory:11964 kb
****************************************************************/


H

这题有个很显然的结论,你如果能凑出区间[0, x],那么对于一个数p,如果x + 1 >= p, 那么你就能凑出区间[0,x + p]的所有数

这题有个加强版的 传送门

#include <cstdio>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <utility>
 
using namespace std;
#define LL long long
#define pb push_back
#define mk make_pair
#define pill pair<int, int>
#define mst(Arr, x) memset(Arr, x, sizeof(Arr))
#define REP(i, x, n)    for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int INF = 1e9 + 10;
int n, m;
LL num[qq];
 
int main(){
    int t;  scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        REP(i, 0, n){
            scanf("%lld", num + i);
        }
        LL money = 0;
        sort(num, num + n);
        REP(i, 0, n){
            if(num[i] <= money + 1){
                money += num[i];
            }else{
                break;
            }
        }
        printf("%lld\n", money);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1231
    User: Yokile
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:0 kb
****************************************************************/




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值