第十七届中国计量大学程序设计竞赛(同步赛)

第十七届中国计量大学程序设计竞赛(同步赛)

题目ABCDEFGHIJKLMN
solved🚫🚫-🚫---

✔:比赛时通过;🚫:赛后通过;⚪:比赛时尝试了未通过

总结:

  • 第一次满员一起打比赛,也还没什么配合,还没有特别明确的分工,就是谁先读懂题意谁先来做。
  • 不要只有一个人懂某块知识。当队友在某道题陷进去之后,需要另一个人来帮忙debug或者是提供测试数据都行,千万别让他一个人陷进去出不来。
  • K题。这个单独写出来,实在是因为温哥哥做太久了,留给温哥哥自己反思。

A A Party for You Started【dfs序+区间更新】

solved by Sstee1XD

题意: 给你一棵树,每次操作先询问单点权值,然后增加一点权值,同时它所有的子节点也增加相同的权值。

题解: 看题意很容易想到单点修改,区间求和或者区间修改,查询单点问题。为了把节点和它的子节点放在一个区间内,要用dfs序去处理。当时写了一个树状数组去区间求和,比较麻烦。赛后发现区间修改,查询单点简单很多,可以用线段树写。这里以树状数组维护差分数组,查询单点为例。

#include<bits/stdc++.h>
#define bug cout << "bug********************************\n"

using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const int maxn = 2e4 + 7;

inline int read(){
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

int n, m, fa, num, rt;
vector<int> G[maxn];
int id[maxn], to[maxn];
ll sum[maxn];
int lowbit(int x) {
	return x & -x;
}

void add(int i, ll x) {
	while (i <= n) {
		sum[i] += x;
		i += lowbit(i);
	}
}

ll query(int i) {
	ll res = 0;
	while (i > 0) {
		res += sum[i];
		i -= lowbit(i);
	}
	return res;
}

void dfs(int u, int fa) {
	id[u] = ++num;
	for (auto v : G[u]) {
    if (v != fa) dfs(v, u);
	}
  to[u] = num;
}

void run(){
	for (int i = 1;i <= n; ++i) {
		fa = read();
		G[fa].push_back(i);
	}
	rt = G[0][0];
	dfs(rt, -1);
	int u, v;
	ll x;
	while (m--) {
		scanf("%d %lld %d", &u, &x, &v);
		printf("%lld\n", query(id[v]));
		add(id[u], x);
    	add(to[u] + 1, -x);
	}
}

int main(){
	n = read();
	m = read();
	run();
	return 0;
}

B Broken Pad【思维】

solved by lllllan

题意: 每组数据含有两个数组a、b,数组由0和1组成。要求你通过一些翻转使得数组a变成数组b。操作要求为:当翻转第i个数字的时候,第i以及他之后的所有数字都将由1变成0或由0变成1。特别的,可以在第一步时,将整个数组重置为全0数组。找出翻转次数最少的操作方案,并输出每次翻转的数字的下标。(如一开始将数组a重置为0,则需要首位输出0)。

题解: 定义一个翻转标记p来记录前一位的翻转对后一位的影响,需要分别去统计一般情况下需要翻转的次数,和重置之后需要翻转的次数,比较之后输出次数较少的即可。

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

int _T;
void run(){
	int q = 0, p = 0;
	string a, b;
	vector<int> ans, res;
	ans.push_back(0);	//ans中记录重置之后的翻转情况,需要先插入0
	cin >> a >> b;
	for(int i = 0; i < a.length(); i++){
		if((q + b[i]) % 2)	ans.push_back(i + 1), q++;
		if((p + a[i] + b[i]) % 2)	res.push_back(i + 1), p++;
	}
	if(ans.size() >= res.size())
		for(int i = 0; i < res.size(); i++)	cout << res[i] << " ";
	else
		for(int i = 0; i < ans.size(); i++)	cout << ans[i] << " ";
	cout << endl;
}

int main(){
	scanf("%d", &_T);
	while(_T--) run();
	return 0;
}

C Cook Steak【贪心】

solved by lllllan

题意: 需要按顺序煎熟n块牛排,而每块牛排 i 都有它合适的温度范围[ l i , r i l_i,r_i li,ri]。温度从0开始,每上升或下降一个单位需要一分钟,到达合适范围之后,煎熟一块牛排需要一分钟。问按顺序煎熟所有的牛排最少需要多少分钟。

题解: 贪心。设一个前驱温度pre,初始为0。按顺序给出n块牛排,如果pre本来在下一块牛排合适的温度范围内,则不做改变。否则,则将温度改变到离pre最近,又在合适范围内的值。最后再算上煎熟n块牛排的时间即答案。

#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;

int _T, n, l, r;
void run() {
	int pre = 0;
	ll ans = 0;
	scanf("%d", &n);
	for(int i = 0; i < n; i++) {
		scanf("%d%d", &l, &r);
		if(pre >= l && pre <= r)	continue;
		if(abs(l - pre) < abs(r - pre)) {
			ans += (ll)(abs(l - pre));
			pre = l;
		} else {
			ans += (ll)(abs(r - pre));
			pre = r;
		}
	}
	ans += (ll)n;
	printf("%lld\n", ans);
}

int main() {
	scanf("%d", &_T);
	while(_T--) run();
	return 0;
}

D Dessert Time【博弈】

solved by Sstee1XD

题意: n n n个装着一定数量蛋糕的盘子,两个人轮流取一个盘子,每次取的盘子中的蛋糕数量不能少于上一个人取的数量。特殊的,如果剩下的盘子中的蛋糕数量都比上一次少,则取的人会一次性把剩下的盘子都取完,称为兜底。谁先取完谁输,问先手是否能赢,输出第一次取的盘子中的蛋糕数量或者 − 1 -1 1

题解: 若所有盘子中的蛋糕数量都一样,若为偶数先手必赢。我们由此向后推,发现若所有相同蛋糕数量的盘子都为偶数时,先手必赢,取蛋糕数量最少的盘子。若出现了奇数,先手为了不让自己兜底,只能取从大到小第一次数量为奇数的盘子。此时若这个盘子中蛋糕数量是最少的,那么先手会取完最后一盘或者兜底,必输;若不是最少的,后手必兜底,先手必胜。

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 7;

int n;
int a[maxn];

void solve() {
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    sort(a + 1, a + 1 + n, greater<int>());
    for (int i = 1; i <= n; ++i) {
        int f = 1;
        while (a[i] == a[i + 1] && i <= n) {
            i++;
            f++;
        }
        if (f & 1) {
            if (i == n) {
                puts("-1");
                return;
            }
            printf("%d\n", a[i]);
            return;
        }
    }
    printf("%d\n", a[n]);
}

int _T;

int main() {
    scanf("%d", &_T);
    while (_T--) {
        scanf("%d", &n);
        solve();
    }
    return 0;
}

E Eat Grapes【博弈】

solved by Sstee1XD

题意: 有一串链式葡萄,每个节点上都有若干颗葡萄。特殊的,对于最后一个节点,会额外连接一颗葡萄,但不算在它给你的数量里(例如有一个节点葡萄数量为 0 0 0,当它成为最后一个节点时,数量会变成 1 1 1)。两个人轮流取葡萄,每次可以在最后一个节点上取正整数个葡萄,当最后一个节点的葡萄数为 0 0 0,则倒数第二个节点就会成为最后一个节点,谁取完谁输,问先手是否能赢。

题解: 当节点上葡萄数为0时,只能直接取,其余情况下,都可以根据情况选择取完,或者取到只剩下一个葡萄让对手取,从而改变接下来会取到的节点的情况。因此谁先取到葡萄数不为0的节点,就能根据接下来的情况选择取法,达到必胜的状态。

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e5 + 7;

int n, a[maxn];

void solve() {
	scanf("%d", &n);
	int res = 0;
	int m ;
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = n; i >= 1; --i) {
		res ^= 1;
		if (a[i]) break;
	}
	if (res) puts("these are sweet grapes");
	else puts("these are sour grapes");
}

int _T;

int main() {
	scanf("%d", &_T);
	while (_T--) solve();
	return 0;
}

F Flag Scramble Competition【观察】

solved by Tryna

题意: 没有输入,直接输出题目文本中出现次数最多的字符。

题解: 队友有办法用python快速地找出来,但是因为训练比较随意,我从a试到了e就通过了。

#include<iostream>
using namespace std;

int main(){
    printf("e\n");
    return 0;
}

H Happy Time is Always Short【线段树】

solved by Sstee1XD

题意: 每次进行区间清 0 0 0,并询问所有数中的最大值。

题解: 线段树区间更新,查询最大值。

#include<bits/stdc++.h>
#define bug cout << "bug********************************\n"

using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;

const int maxn = 1e5 + 7;

int a[maxn];

struct SEG{
	int t[maxn << 2];
	int lazy[maxn << 2];
	
	void up(int id) {
		t[id] = max(t[id << 1], t[id << 1 | 1]);
	}
	
	void build(int id, int l, int r) {
		t[id] = lazy[id] = 0;
		if (l == r) {
			t[id] = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		build(id << 1, l, mid);
		build(id << 1 | 1, mid + 1, r);
		up(id);
	}
	
	void down(int id) {
		if (lazy[id] == 0) return;
		lazy[id << 1] = lazy[id << 1 | 1] = -1;
		t[id << 1] = t[id << 1 | 1] = 0;
		lazy[id] = 0;
	}
	
	void modify(int id, int l, int r, int ql, int qr) {
		if (ql <= l && qr >= r) {
			t[id] = 0;
			lazy[id] = -1;
			return;
		}
		down(id);
		int mid = (l + r) >> 1;
		if (ql > mid) modify(id << 1 | 1, mid + 1, r, ql, qr);
		else if (qr <= mid) modify(id << 1, l, mid, ql, qr);
		else modify(id << 1, l, mid, ql, qr), modify(id << 1 | 1, mid + 1, r, ql, qr);
		up(id);
	}
	
	int query(int id, int l, int r, int ql, int qr) {
		if (ql <= l && qr >= r) return t[id];
		down(id);
		int mid = (l + r) >> 1;
		if (ql > mid) return query(id << 1 | 1, mid + 1, r, ql, qr);
		else if (qr <= mid) return query(id << 1, l, mid, ql, qr);
		else return max(query(id << 1 | 1, mid + 1, r, ql, qr), query(id << 1, l, mid, ql, qr));
	}
}seg;

int _T, n, m;
int ql, qr;
void run(){
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	seg.build(1, 1, n);
	while (m--) {
		scanf("%d %d", &ql, &qr);
		seg.modify(1, 1, n, ql, qr);
		printf("%d\n", seg.query(1, 1, n, 1, n));
	}
}

int main(){
	scanf("%d", &_T);
	while (_T--) run();
	return 0;
}

I Isolated Pointset【签到】

solved by Tryna

题意:给你若干个点,问你是否存在两个点的垂直平分线通过任意一个点
题解:除了1,2不满足,其余情况都存在。

#include<bits/stdc++.h>
#include<ctime>
using namespace std;
const int maxn=10010;
int t,n;
int main(void){
     scanf("%d",&t);
     while(t--){
         scanf("%d",&n);
         if(n==1||n==2) printf("No\n");
         else printf("Yes\n");
     }
}

J Jiufeng’s Football Team

solved by Sstee1XD

题意:给你一张完全无向图,以及 m m m条边和边权值,没有涉及的边权值为 0 0 0,把 n n n个点分成数量可以不相同的两组,问同组内最大边权最小值。
题解:二分边权,然后考虑补图,将大于等于这个权值的边的两个顶点进行染色,找到能将补图染色成功的最小的边权值,答案即为下一个权值大小。注意每次要将第 m + 1 m+1 m+1条边的权值设为 0 0 0,不然二分到最后的时候输出第 m + 1 m+1 m+1条边的权值会 w a wa wa

#include <bits/stdc++.h>

using namespace std;

const int maxn = 2e4 + 7;
const int maxm = 1e5 + 7;

struct Edge{
	int u, v, w;
	bool operator < (const Edge &e) const{
		return w > e.w;
	}
}edge[maxm];

struct G{
	int nxt, v;
}g[maxm << 1];

int cnt, head[maxn];

void addedge(int u, int v) {
	g[cnt] = {head[u], v,};
	head[u] = cnt++;
	g[cnt] = {head[v], u};
	head[v] = cnt++;
}

int n, m, vis[maxn], _T;

int dfs(int u, int c, int fa) {
	if (vis[u]) return vis[u] == c;
	vis[u] = c;
	for (int i = head[u]; ~i; i = g[i].nxt) {
		int v = g[i].v;
		if (v == fa) continue;
		if (!dfs(v, -c, u)) return 0;
	}
	return 1;
}

int work(int rear) {
	memset(vis, 0, sizeof(vis));
	memset(head, -1, sizeof(head));
	cnt = 0;
	for (int i = 1; i <= rear; ++i) {
		addedge(edge[i].u, edge[i].v);
	}
	for (int i = 1; i <= n; ++i) {
		if (vis[i]) continue;
		if (!dfs(i, 1, 0)) return 0;
	}
	return 1;
}

void solve() {
	scanf("%d %d", &n, &m);
	for (int i = 1, u, v, w; i <= m; ++i) {
		scanf("%d %d %d", &u, &v, &w);
		edge[i] = {u, v, w};
	}
	sort(edge + 1, edge + 1 + m);
	int l = 1, r = m, mid;
	edge[m + 1].w = 0;
	while (l <= r) {
		mid = l + r >> 1;
		if (work(mid)) l = mid + 1;
		else r = mid - 1;
	}
	printf("%d\n", edge[l].w);
}

int main() {
	scanf("%d", &_T);
	while (_T--) solve();
}

K Known-Well Palindrome Date-Easy Version【回文串】

solved by Tryna

题意:一行给你若干串数字,让你判断有多少个回文日期,回文日期必须要满足以下两个条件。1.必须是一个合法的日期,即不能有1月32日这样的数字出现。2.必须满足是一个回文串。
题解:读入一行存进一个string中,从第0个位置暴力遍历到st.size()-7的位置,条件老老实实判断,比赛过程中因为没有判断年份是否合法wa了很多次。早上又想了一下发现其实不用判断闰年,闰年和平年只有2月不一样,如果是二月,那该年份一定是闰年 XX2002XX。

#include<bits/stdc++.h>
using namespace std;
const int maxn=210;
int a[20]={0,31,29,31,30,31,30,31,31,30,31,30,31};
string st;
int cnt,ans;
int main(void){
     ios::sync_with_stdio(false);
     cin.tie(0);cout.tie(0);
     while(getline(cin,st)){
          ans = 0;
          if (st == "#") break;
          if (st.size() < 8) {
              cout << 0 << endl;
              continue;
          }
          for(int i = 0; i < st.size() - 7; i++){
             if((st[i] == st[i + 7]) && (st[i + 1] == st[i + 6] )&& (st[i + 2] == st[i + 5]) && (st[i + 3] == st[i + 4])){
                 int y = (st[i]-'0')*1000+(st[i+1]-'0')*100+(st[i+2]-'0')*10+(st[i+3]-'0');
                 int m = (st[i + 4] - '0') * 10 + (st[i + 5] - '0');
                 int d = (st[i + 6] - '0') * 10 + (st[i + 7] - '0');
                 if (y < 1 || y > 9999) continue;
                 if((m >= 1 && m <= 12) && (d >= 1 && d <= a[m])) ans++;
             }
          }
          cout << ans << endl;
    }
     return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值