第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)(重现赛)

第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)(重现赛)

导语

日常的队内集训,开始的时候状态其实很好,但是到了后两题就出现了状况,其实完全可以至多错两发就过的,应该快接近铜牌水平了,希望能在今年济南站首次拿牌

链接:第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(济南)(重现赛)

涉及的知识点

思维,贪心,二分图,位运算

题目

C

题目大意:n堆石子,每堆石子最多有3个,现在将这n堆石子合并,每次选择两堆来合并,合并两堆的花销为 ( x % 3 ) ( y % 3 ) (x\%3)(y\%3) (x%3)(y%3),求出最小花销

思路:和队友讨论得出结论后然后让队友写的,结论很简单,一开始个数为3的是不用考虑的,直接考虑个数为2和个数为1即可,将2和1对应组合,判断剩余的是2还是1,再根据对应的情况直接处理即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    ll a,b,c,res=0;
    cin >>a>>b>>c;
    res+=min(a,b)*2;
    if(a>b) {
        res+=(a-b)/3*3;
        if((a-b)%3==2)res++;
    } else {
        res+=(b-a)/3*6;
        if((b-a)%3==2)res+=4;
    }
    cout <<res;
    return 0;
}

D

题目大意:n个学生, w i w_i wi为第i个学生写的论文字数,第i个学生有一个字数下限 L i L_i Li和一个字数上限 R i R_i Ri,显然有 L i ≤ w i ≤ R i L_i\le w_i\le R_i LiwiRi,对第i个学生,它的成绩是 n − K i n-K_i nKi K i K_i Ki是n个学生中满足 w j > w i w_j\gt w_i wj>wi的j的数量,每个学生想要实现更高的分数,所以对他们最有利的情况下他们都会写到自己的上限,但显然,如果每个人写的字数都一样,所有人的成绩都是最高分,现在为了避免内卷,每个学生需要选择一个 w i w_i wi满足这两个条件:

  1. 对于每个学生,他/她的成绩不能比最初的计划低
  2. 最小化 ∑ i = 1 n w i \sum_{i=1}^n w_i i=1nwi

思路:这题是队友写的,好像是因为读题的问题忽略了细节,结果犯了不该犯的错误,以后还是尽量自己来看一遍翻译
这里有一个很关键的一个地方,就是不能比最初的计划低,最初的计划是都取右端点,所以首先按照右边界排序获得最初计划的乘积,

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e5+5;
typedef pair<ll,ll>pr;
pr ran[N];
bool cmp(pr p1,pr p2) {
    if(p2.second > p1.second)
        return true;
    else if(p2.second == p1.second)
        return p2.first > p1.first;
    else
        return false;
}
int main() {
    int n;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
        scanf("%lld %lld",&ran[i].first,&ran[i].second);
    sort(ran+1,ran+1+n,cmp);
    ll res=0,tmp=0;
    for(int i=1; i<=n;) {
        ll rmax=ran[i].second,mark=ran[i].first,cnt=0;
        while(i<=n) {
            if(ran[i].second!=rmax)
                break;
            cnt++;
            mark=max(max(mark,ran[i].first),tmp);
            i++;
        }
        res+=(cnt*mark);
        tmp=mark;
    }
    printf("%lld\n",res);
    return 0;
}

G

题目大意:给出两个正整数 X , Y ( X > Y ) X,Y(X>Y) X,Y(X>Y),每次操作 X = X x o r A ( 0 ≤ A < X ) X=XxorA(0\le A \lt X) X=XxorA(0A<X),现在要操作至多5次使得 X X X变成 Y Y Y

思路:首先, X x o r Y x o r X = Y X xor Y xorX=Y XxorYxorX=Y,那么理论上进行两次操作即可,如果 X x o r Y > X XxorY>X XxorY>X,那么先算 X x o r Y XxorY XxorY即可,否则就直接把 X x o r Y XxorY XxorY的结果与 X X X异或即可

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
    ll X,Y;
    scanf("%lld%lld",&X,&Y);
    ll xy=X^Y;
    if(xy>X)
        printf("2\n%lld %lld",Y,X);
    else
        printf("1\n%lld",xy);
    return 0;
}

J

题目大意:有一个图构造器,输入一个整数序列 a 1 … n ( 0 ≤ a < 2 60 ) a_{1\dots n}(0\le a\lt 2^{60}) a1n(0a<260),输出一个有n个节点的无向图,当 a x ∣ a y = 2 60 − 1 时 , 有 a_x|a_y=2^{60}-1时,有 axay=2601 ( x , y ) (x,y) (x,y)存在,现在给出一棵有n个节点的树,求出构造整棵树的序列

思路:训练的时候考虑的方向是对的,但是没有解决图中的极端情况,给定节点数很小,考虑按位来构造这棵树,因为是或运算,贪心的去想,尽量保证节点之间所取的位不同,那么,每个节点与相连节点至少有两位不同,道理很简单,如果只有一位不同的话,那么该节点的相连节点会全部相同,下一层就没办法推出了。如图,可以看到每隔一层的节点,它们的位上01的情况应该是大致相同的,不过是几位不同而已,这里就可以用二分图的思想把所有节点染色,再根据颜色少的节点来构造,其数量必定是小于等于50个的,假设红色的即为颜色少的节点颜色,构造方法如下(不止这一种):

  1. 第i个红点最高位置0,第i为0,其余为1,红点之间无边
  2. 蓝点最高位置1,与其相邻的红点对应编号为x,蓝点所有的x置1,其余为0,蓝点之间无边

在这里插入图片描述在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
int n,cnt,head[1212],id[121];
long long a[121];
vector<int>vec[2];
struct node {
    int next,to;
} e[1212];
void Add(int from,int to) {
    e[++cnt].next=head[from];
    e[cnt].to=to;
    head[from]=cnt;
}
void dfs(int u,int fa,int col) {//染色
    vec[col].push_back(u);
    for(int i=head[u]; i; i=e[i].next) {
        int v=e[i].to;
        if(v!=fa)
            dfs(v,u,col^1);
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >>n;
    for(int i=1; i<n; i++) {
        int u,v;
        cin >>u>>v;
        Add(u,v);
        Add(v,u);
    }
    dfs(1,0,0);
    if(vec[0].size()>vec[1].size())swap(vec[0],vec[1]);//确保以少的点为基础
    int len=vec[0].size();
    for(int i=0; i<len; i++) {//白点加上权值
        a[vec[0][i]]=(1ll<<60)-1-(1ll<<i)-(1ll<<59);
        id[vec[0][i]]=i;//标记
    }
    len=vec[1].size();
    for(int i=0; i<len; i++) {
        int u=vec[1][i];
        a[u]=(1ll<<59);//最高位设1
        for(int j=head[u]; j; j=e[j].next) {//根据邻接点加上缺的值
            int v=e[j].to;
            a[u]+=(1ll<<id[v]);
        }
    }
    for(int i=1; i<=n; i++)
        cout <<a[i]<<" ";
    return 0;
}

M

题目大意:一个锅,每次可以同时煎K张饼,每个饼正反面都要简,每次剪花费1h,求煎完所有n张的最小花费

思路:就是个找规律的题,思路很简单

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, k;
    scanf("%d%d",&n,&k);
    if(n<=k)
        printf("2\n");
    else if(2*n%k)
        printf("%d\n",2*n/k+1);
    else
        printf("%d\n",2*n/k);
    return 0;
}

参考文献

  1. 2020 ICPC济南站 J Tree Constructer 构造+二分图染色
  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值