Codeforces Round #693 (Div. 3)

58 篇文章 0 订阅
13 篇文章 1 订阅

A. Cards for Friends

题意:有一张 w*h 的卡片,现在要把这张卡片剪成不少于n片,如果w为偶数那么可以沿着w一分为二,如果h为偶数同理。

思路:只有偶数可以剪。对w和h单独考虑可以剪几次。剪一次,个数就乘以2。所以对w和h一直除2,答案一直乘2就行了。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 3e5+17;
int a[300050];
int n;
int cnt = 0;
int prime[N];
int mark[N];
void pre(){
    mark[1] = 1;
    for(int i = 2 ; i < 100050 ; i ++){
        if(!mark[i]) prime[cnt++] = i;
        for(int j = 0 ; prime[j]*i < 100050 ; j ++){
            mark[prime[j]*i] = 1;
            if(i%prime[j] == 0) break;
        }
    }
}
 
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        int w,h,n;
        cin>>w>>h>>n;
        int ans = 1;
        while(w && w%2 == 0){
            w /= 2;
            ans *= 2;
        }
        while(h && h%2 == 0){
            h /= 2;
            ans *= 2;
        }
        ans = ans >= n;
        puts(ans ? "YES" : "NO");
    }
    return 0;
}

B. Fair Division

题意:有很多2和1,现在要把他们分成两堆,使得两堆的和相等。

思路:显然要先求和嘛,如果和是奇数那就扯淡了。如果是偶数,数一下有多少个2和1。然后看和的一半是奇数还是偶数。如果是奇数,并且1的个数为0,那么不可能成立。其他情况都是成立的。

如果是奇数,且1的个数不为1,那么只要一边分到至少一个1就行了。
如果是偶数,那随便乱分都是成立的,因为和是偶数,一半也是偶数,随便分。

AC代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 3e5+17;
int a[300050];
int n;
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        int sum = 0;
        int cnt = 0;
        for(int i = 0 ; i < n ; i ++){
            cin>>a[i];
            sum += a[i];
            if(a[i] == 1) cnt ++;
        }
        if(sum%2){
            cout<<"NO"<<endl;
        }else{
            if((sum/2)%2 && cnt == 0){
                cout<<"NO"<<endl;
            }else{
                cout<<"YES"<<endl;
            }
        }
    }
    return 0;
}

C. Long Jumps

题意:一个数组,初始可以选择一个位置,然后可以向右跳a[i]格,一直跳到不能跳。得到的价值就是所有经过的元素的和。

思路:类似前缀和,把每个格子的值,加到 i+a[i] 的格子上去。然后找最大值就行了。或者从后往前加也行,每次自己加上 i+a[i] 格子的值。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 3e5+17;
int a[300050];
int sc[300050];
int n;
int cnt = 0;
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 0 ; i < n ; i ++){
            cin>>a[i];
            sc[i] = 0;
        }
        int maxx = 0 ;
        for(int i = n-1 ; i >= 0 ; i --){
            sc[i] = a[i];
            if(i+a[i] < n){
                sc[i] += sc[i+a[i]];
            }
            maxx = max(sc[i],maxx);
        }
        cout<<maxx<<endl;
    }
    return 0;
}

D. Even-Odd Game

题意:一个数组,如果A选了一个偶数,那么他的得分就可以加这个偶数。如果B选了一个奇数,那么他加上这个奇数的分。否则选了不得分。求谁能够得的分更高。

思路:类似于从两头取数,比谁取得和更大。可以这样想,如果A拿走了一个奇数,相等于让B得不到这个分。所以再选择的时候,就挑最大的拿,如果是我要的,那么我可以加上这个分,如果不是我要的,那么等于我偷走你的分。还是赚了。所以贪心选择当前最大值就好了。所以排个序对奇偶位置上的数求和就完了。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 3e5+17;
int a[300050];
int sc[300050];
int n;
int cnt = 0;
 
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 0 ; i < n ; i ++){
            cin>>a[i];
        }
        sort(a,a+n);
        int p = 1;
        int al = 0;
        int bo = 0;
        for(int i = n-1 ; i >= 0 ; i --){
            if(p && a[i]%2 == 0){
                al += a[i];
            }else if(!p && a[i]%2){
                bo += a[i];
            }
            p = p^1;
        }
        if(al > bo){
            cout<<"Alice"<<endl;
        }else if(al < bo){
            cout<<"Bob"<<endl;
        }else{
            cout<<"Tie"<<endl;
        }
    }
    return 0;
}

E. Correct Placement

题意:有n个wi*hi 的方块,然后可以横着放可以竖着放。求 每个方块前面放置 谁,可以不被挡住。不被挡住定义是,前面的方块的长宽都比后面的方块更小。

思路:先把每个方块拆成两块,但是他们的id还是同一个,然后进行排序。先按h排序,然后按w排序。这样 第一个方块一定是 长宽都是最小的吧。然后。往后遍历,h一定是大于等于他的,但是w有可能是小于他的,所以这个时候,要记得更小更小的w值。这样可以保证 已经选择的h一定小于等于当前方块,并且w是最小的。 就是最优解了。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 4e5+17;
//int a[300050];
int n;
int cnt = 0;
struct node{
    int w,h;
    int id;
}a[N];
int res[N];
 
bool cmp(node a1,node a2){
    if(a1.h == a2.h){
        return a1.w < a2.w;
    }
    return a1.h < a2.h;
}
 
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 0; i < n ; i ++){
            cin>>a[i].h>>a[i].w;
            a[i+n].h = a[i].w;
            a[i+n].w = a[i].h;
            a[i+n].id = i;
            a[i].id = i;
            res[i] = -1;
        }
        n *= 2;
        sort(a,a+n,cmp);
//        for(int i = 0 ; i < n ; i ++){
//            cout<<a[i].h<<" -++- "<<a[i].w<<" ** "<<a[i].id+1<<endl;
//        }
        res[a[0].id] = -1;
        int pre = 0;
        int minn = 0;
        int ww = a[0].w;
        for(int i = 1 ; i < n ; i ++){
            if(a[pre].h < a[i].h && a[pre].w < a[i].w){
                res[a[i].id] = a[pre].id+1;
            }
            if(i == n-1) break;
            if(a[i].w < ww){
                ww = a[i].w;
                minn = i;
            }
            if(a[i+1].h != a[minn].h){
                pre = minn;
            }
        }
        for(int i = 0; i < n/2 ; i ++){
            cout<<res[i]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

F. New Year’s Puzzle

题意:给定一个2*n的矩形。不过有一些块被ban掉了。然后用 2*1 和 1*2 的砖头把他铺满。问能否铺满。在这里插入图片描述

思路:其实就一个分类讨论的题。先对黑块排个序。然后遍历。分很多种情况。先假设 up = 1表示 底下是黑块,而上面放了一个躺着的块。 down = 1表示下面放了一个躺着的块。来表示两种状态。用 last 表示 上一个黑块的位置 如图:

在这里插入图片描述

然后分类讨论了。

1. 上下都是黑块。那前面必须 down 和 up 都为0才行。也就是不会能铺满2*k的格子而不会凸出来一个。
2. 上面是黑块。有五种情况,第一种就是down 和 up 都为0,那么必须放一个躺着的,剩下的情况:中间的白块数量只是代表距离的奇偶性。

在这里插入图片描述

3. 下面是黑块。有五种情况,第一种同上就是down 和 up 都为0,那么必须放一个躺着的,剩下的情况。 和上面一样分析。 倒过来就行了。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 1e6+7;
int n,m,k,t = 1,cas = 1;
//int a[N],b[N];
struct node{
    int x,y;
}a[N];

bool cmp(node a1,node a2){
    return a1.y < a2.y;
}

signed main(){
    cin>>t;
    while(t--){
        cin>>n>>m;
        int maxx = 0;
        for(int i = 0 ; i < m ; i ++){
            cin>>a[i].x>>a[i].y;
            maxx = max(a[i].y,maxx);
        }
        a[m].x = 1,a[m++].y = maxx+1;
        a[m].x = 2,a[m++].y = maxx+1;
        sort(a,a+m,cmp);
        int up = 0;
        int down = 0;
        int flag = 1;
        int last = 0;
        for(int i = 0; i < m ; i ++){
            //cout<<a[i].x<<" "<<a[i].y<<" "<<up<<" "<<down<<endl;
            if((i+1) < m && a[i+1].y == a[i].y){
                if(up || down){
                    flag = 0;
                    break;
                }
                i ++;
            }else if(a[i].x == 1){
                int dis = a[i].y - last - 1;
                if(dis%2){  // 距离为奇数
                    if(down == 0 && up == 0){
                        down = 1;
                    }else if(down){
                        flag = 0;
                        break;
                    }else{
                        up = 0;
                        down = 0;
                    }
                }else{      // 距离为偶数
                    if(down == 0 && up == 0){
                        down = 1;
                    }else if(down){
                        up = 0;
                        down = 0;
                    }else{
                        flag = 0;
                        break;
                    }
                }
            }else{
                int dis = a[i].y - last - 1;
                if(dis%2){  // 距离为奇数
                    if(up == 0 && down == 0){
                        up = 1;
                    }else if(up){
                        flag = 0;
                        break;
                    }else{
                        up = 0;
                        down = 0;
                    }
                }else{      // 距离为偶数
                    if(up == 0 && down == 0){
                        up = 1;
                    }else if(up){
                        down = 0;
                        up = 0;
                    }else{
                        flag = 0;
                        break;
                    }
                }
            }
            last = a[i].y;
        }
        puts(flag ? "YES" : "NO");
    }
}



G. Moving to the Capital

题意:一个有向图。首都为1。然后每个点到首都都有一个最短距离。从某个点出发,求可以走到离首都最近的距离是多少。只有两种走法。但是走法2,最多只能执行一次,走法1可以执行无限次。

1. 走到最短距离更远的点
2. 走到最短距离小于等于当前最短距离的点

思路:因为边权都为1,那么dfs算出每个点的最短距离先。然后就是树形dp了。考虑一个点,可以走到比自己更远的点,但是只能都到更近或者相同的点一次。如果进行了操作2,那么下次只能走到距离更远的点了,那不如不走了。所以呢就结束了。所以对于操作2呢,直接取目的点的最短距离就好了。但是对于更远的点,就必须要求更远的点已经更新过了,才能用来更新当前点。所以dp的顺序按照远近顺序来。远的先更新。就没问题了。

AC代码:

#include <bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 4e5+17;
int inf = 1e18;
int a[300050];
int n,m;
int cnt = 0;
int mark[N];
int vis[N];
int dis[N];
vector<int> edge[N];
 
void dfs1(int pos,int sum){
    queue<pair<int,int> > que;
    que.push(make_pair(1,0) );
    vis[1] = 1;
    while(!que.empty()){
        int now = que.front().first;
        int st = que.front().second;
        dis[now] = st;
        que.pop();
        for(int i = 0 ; i < edge[now].size() ; i ++){
            int to = edge[now][i];
            if(!vis[to]){
                vis[to] = 1;
                que.push(make_pair(to,st+1));
            }
        }
 
    }
}
 
int dp[N];
 
void dfs2(int pos){
    vis[pos] = 1;
    dp[pos] = dis[pos];
    for(int i = 0 ; i < edge[pos].size() ; i ++){
        int to = edge[pos][i];
        if(!vis[to] && dis[pos] < dis[to]){
            dfs2(to);
        }
        if(dis[pos] < dis[to]){
            dp[pos] = min(dp[pos],dp[to]);
        }else{
            dp[pos] = min(dp[pos],dis[to]);
        }
    }
}
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i = 1 ; i <= n ; i ++){
            edge[i].clear();
            vis[i] = 0;
            dis[i] = inf;
            dp[i] = inf;
        }
        for(int i = 0 ; i < m ; i  ++){
            int x,y;
            cin>>x>>y;
            edge[x].push_back(y);
        }
        dfs1(1,0);
        for(int i = 1 ; i <= n ; i ++){
            vis[i] = 0;
        }
        dfs2(1);
        for(int i = 1 ; i <= n ; i ++){
            cout<<dp[i]<<" ";
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值