「团队训练赛」2021 Jiangsu Collegiate Programming Contest题解

A – Spring Couplets

题目描述:

写春联,满足所需的平仄关系

如果上联的一个字是平的,那下联对应的字必须是仄的

相同的,如果上联的一个字是仄的,那下联对应的字必须是平的

而且上联的最后一个字必须是仄的

问你春联是否合法

思路:

就把数搞出来以后,挨个判断就行

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

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
string s;
int ar[MAX];
int br[MAX];
void work(){
    cin >> n;
    for(int i = 1; i <= n; ++i){
        cin >> s;
        ar[i] = s.back() - '0';
    }
    for(int i = 1; i <= n; ++i){
        cin >> s;
        br[i] = s.back() - '0';
    }
    for(int i = 1; i < n; ++i){
        if(ar[i] == 1 || ar[i] == 2){
            if(br[i] == 3 || br[i] == 4)continue;
            else {
                cout << "NO\n";
                return;
            }
        }
        else if(ar[i] == 3 || ar[i] == 4){
            if(br[i] == 1 || br[i] == 2)continue;
            else {
                cout << "NO\n";
                return;
            }
        }
    }
    if(ar[n] == 3 || ar[n] == 4){
        if(br[n] == 1 || br[n] == 2){
            cout << "YES\n";
            return;
        }
    }
    cout << "NO\n";
}


int main(){
    io;
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}

C – Magical Rearrangement

题目描述:

给你若干个数字,问能凑出来的最小的数字是多少

如果不行的话,输出-1

思路:

先考虑-1的情况,用maxn记录数量最大的数字,用sum记录除maxn的数字外的数字的和

  • 如果maxn是0,由于第一位不能放0的限制,则必须满足sum >= maxn
  • 如果maxn不是0,因为第一位可以放非0的数字,所以只需要满足sum >= maxn - 1即可

然后我们需要一个函数f(x),表示上一位选择x时,下一位可以选择的最小的数,我们只需要从09去枚举每一位,判断如果这一位选这个数时,剩下的数字能否满足条件的摆放,判断条件就和上面的几乎一样

//Work by: Chelsea
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, op;
int tr[MAX];

int update(int x){
    for(int i = 0; i < 10; ++i){
        if(!tr[i] || i == x)continue;
        --tr[i];
        int p = 0, maxn = 0, sum = 0;
        for(int j = 0; j <= 9; ++j){
            sum += tr[j];
            if(maxn < tr[j]){
                maxn = tr[j];
                p = j;
            }
        }
        sum -= maxn;
        if(p == i && maxn > sum){
            ++tr[i];
            continue;
        }
        if(p != i && maxn > sum + 1){
            ++tr[i];
            continue;
        }
        return i;
    }
    return -1;
}

void work(){
    int sum = 0;
    int p = 0, maxn = 0;
    for(int i = 0; i <= 9; ++i){
        cin >> tr[i];
        if(maxn < tr[i]){
            maxn = tr[i];
            p = i;
        }
        sum += tr[i];
    }
    sum -= maxn;
    if(sum == 0 && tr[0] == 1){
        cout << 0 << endl;
        return;
    }
    if(p == 0 && sum < maxn){
        cout << -1 << endl;
        return;
    }
    if(p != 0 && sum < maxn - 1){
        cout << -1 << endl;
        return;
    }
    string ans = "";
    int num = update(0);
    while(num >= 0){
        ans += (char)(num + '0');
        num = update(num);
    }
    cout << ans << endl;
}


int main(){
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}


D – Pattern Lock

题目描述:

给你一个n*m的点阵,你需要用一笔画的形式连接n*m个点,并输出n*m个点的坐标,表示连接顺序,需要满足如下的条件:

  • 连接的点只能出现一次
  • 任意两条相邻的边的夹角必须是锐角
  • 任意两个相邻的点之间不能存在别的点,即(1, 1),(3, 3)之间不能直接连接,因为中间存在一个(2, 2)

思路:

很有意思的一个题

需要根据nm的奇偶来分类讨论

  • 对于n是偶数的情况很好说,只需要按下图来进行连接即可

    image-20220502155826408

  • 对于n是奇数

    • 如果m是偶数,那也很好连,只需要像n是偶数的那样,不过需要把图倒过来

      image-20220502160508264

    • 如果m是奇数,就不是很好搞,需要把奇数的拆成三部分

      一个偶*奇 + 3 * 3 + 3 * 偶

      image-20220502160940989

所以我们只需要搞一个横着的,一个竖着的,还一个3 * 3的就行,写起来蛮简单的

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

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;
#define y1 y114514
#define MAX 300000 + 50
int n, m, k, x;

vector<pii>ans;

void calleft_rigth(int x1, int y1, int x2, int y2){
    ans.push_back(m_p(x1, y1));
    for(int i = 1; i <= y2 - y1; ++i){
        ans.push_back(m_p(x1, y1 + i));
        ans.push_back(m_p(x2, y1 + i - 1));
    }
    ans.push_back(m_p(x2, y2));
}

void calup_down(int x1, int y1, int x2, int y2){
    ans.push_back(m_p(x1, y1));
    for(int i = 1; i <= x2 - x1; ++i){
        ans.push_back(m_p(x1 + i, y1));
        ans.push_back(m_p(x1 + i - 1, y2));
    }
    ans.push_back(m_p(x2, y2));
}

void cal_3(int x, int y){
    ans.push_back(m_p(x, y));
    ans.push_back(m_p(x, y + 1));
    ans.push_back(m_p(x + 2, y));
    ans.push_back(m_p(x + 1, y + 2));
    ans.push_back(m_p(x + 2, y + 2));
    ans.push_back(m_p(x + 1, y + 1));
    ans.push_back(m_p(x + 2, y + 1));
    ans.push_back(m_p(x, y + 2));
    ans.push_back(m_p(x + 1, y));
}

void work(){
    cin >> n >> m;
    if(n % 2 == 0){
        for(int i = 1; i <= n; i += 2){
            calleft_rigth(i, 1, i + 1, m);
        }
    }
    else {
        if(m % 2 == 0){
            for(int i = 1; i <= m; i += 2){
                calup_down(1, i, n, i + 1);
            }
        }
        else{
            for(int i = 1; i <= n - 3; i += 2){
                calleft_rigth(i, 1, i + 1, m);
            }
            cal_3(n - 2, 1);
            for(int i = 4; i <= m; i += 2){
                calup_down(n - 2, i, n, i + 1);
            }
        }
    }
    for(auto [x, y] : ans)cout << x << ' ' << y << endl;

}


int main(){
    io;
    work();
    return 0;
}

I – Fake Walsh Transform

题目描述:

输入n, m,你可以从0 中选择若干个数,使的这些数异或后等于m,问最多可以选多少个数

思路:

  • n = 1

    • m = 0, 输出2
    • m = 1, 输出1
  • n > 1

    • m = 0,输出
    • m = 1,输出
//Work by: Chelsea
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod 1000000007
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d %d",&n,&m)
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
ll n, m;

void work(){
    cin >> n >> m;
    if(n == 1){
        if(m == 0)cout << 1 << endl;
        else cout << 2 << endl;
    }
    else if(m == 0){
        cout << (1ll << n) << endl;
    }
    else{
        cout << (1ll << n) - 1 << endl;
    }
}


int main(){
    int tt;cin>>tt;
    for(int _t = 1; _t <= tt; ++_t){
        work();
    }
    return 0;
}

J – Anti-merge

题目描述:

给你一个n * m的矩阵,每个点都有一个权值,合并矩阵的方法是这样的:

  • 先每一列的选择权值相同的点,合并成一个点
  • 再每一行的看,选择权值相同的点,合并成一个点,如果合并列的时候合并出相邻的两个起点终点相同的权值相同的两个点,则这两个点也可以合并成一个新的点

现在你需要给矩阵上的每个点都加一个标签,这样合并的时候需要满足权值和标签完全相同的来合并,问要使的整个矩阵的任意一个点都不被合并,最少需要几种标签,在此基础上,最小有几个点需要打上标签,点可以不存在标签,如果不存在标签,则默认标签的值为0,这是不计入答案的数量

思路:

其实读懂以后会发现这是个很简单的题,我们只需要一种标签就行

对于每个连通块,我们去01染色就行,染出黑色和白色后,看看哪个数量少,就把数量少的打上标签,数量多的就当不打标签,这样数量就是最小的

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

#define endl '\n'
#define inf 0x3f3f3f3f
#define mod7 1000000007
#define mod9 998244353
#define m_p(a,b) make_pair(a, b)
#define mem(a,b) memset((a),(b),sizeof(a))
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define debug(a) cout << "Debuging...|" << #a << ": " << a << "\n";
typedef long long ll;
typedef pair <int,int> pii;

#define MAX 300000 + 50
int n, m, k, x;
int tr[505][505];
bool vis[505][505];
vector<pii>ans;
vector<pii>num0, num1;
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};

bool judge(int x, int y){
    if(x > n || x < 1 || y > m || y < 1)return false;
    else if(vis[x][y])return false;
    return true;
}

void dfs(int x, int y, int c){
    vis[x][y] = 1;
    if(c == 0)num0.push_back(m_p(x, y));
    else num1.push_back(m_p(x, y));
    for(int i = 0; i < 4; ++i){
        int xx = x + dx[i];
        int yy = y + dy[i];
        if(judge(xx, yy) && tr[x][y] == tr[xx][yy]){
            dfs(xx, yy, c ^ 1);
        }
    }
}

void work(){
    cin >> n >> m;
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            cin >> tr[i][j];
        }
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            if(!vis[i][j]){
                num0.clear();
                num1.clear();
                dfs(i, j, 0);
                if(num0.size() >= num1.size()){
                    for(auto [x, y] : num1){
                        ans.push_back(m_p(x, y));
                    }
                }
                else{
                    for(auto [x, y] : num0){
                        ans.push_back(m_p(x, y));
                    }
                }
            }
        }
    }
    if(ans.size() == 0)cout << 0 << ' ';
    else cout << 1 << ' ';
    cout << ans.size() << endl;
    for(auto [x, y] : ans)cout << x << ' ' << y << ' ' << 1 << endl;
    
}

int main(){
    io;
    work();
    return 0;
}

K – Longest Continuous 1

好像是个打表题,队友写的,这里懒得补了,贴一下他的代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
int t;
long long n;
long long sum=0;
int ans;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld",&n);
		if(n==1)
		{
			printf("0\n");
			continue;
		}
		sum=1;
		ans=1;
		for(int i=1;i<=50;++i)
		{
			sum+=(1ll<<(i-1))*i;
			if(n>=sum+1) ans=i+1;
			else break;
		}
		printf("%d\n",ans);
	}
	return 0;
}

日常赛后小作文

还是因为疫情,没法在集训室统一实行3人1机,就还是打的三人三机。

我们队伍签到挺快的,我和djk一起开A题,yj去开K题,我上来没怎么看题面,直接看样例猜题意,发现是个春联的平仄问题,问了djk一句谁是平谁是仄以后就直接写了,7分钟的时候就过了

然后我和djk就去看I题,这个题当时太急了,没考虑仔细,wa了3发,后来才改出来,我的锅,都怪我没事瞎试,在比赛最开始贡献了60发的罚时,下次比赛在前期一定会多检查检查再交,尽量1a

我和djk又开C,我发现这个题有0的存在不好写,感觉有点模拟的意思,就不想写,就去看J

这个时候yj过了K题,我们就让他去写C

我和djk开J,读完题想了一会就想到了01染色,把数量小的记为答案,然后我火速写了就过了,这个时候大概是1小时17分

我写J的时候,djk去读D了,我过了J后我俩就开始讨论怎么D,开始他糊了个假做法给我,我直接去敲了,敲了个头的时候,yj过了C,djk就把题意和思路讲个yj,yj一眼就发现这个思路的问题,得亏发现的早,不然就白写了

我们三就开始疯狂画图讨论,讨论了有快一个小时,画了写写了画,终于讨论出来四种情况改怎么写,然后两个人都写了,都wa了,我和djk讨论觉得一致没问题,甚至怀疑到数据有问题,笑死,后来我改了一下3 * 3的矩阵构造方法,就过了,然后就发现了我们的一个小问题,由于我们俩都是用的我画出来的图写的,所以俩人都写挂了,这个时候大概是赛后的3点45,打完就下班了

博客内容均系原创,未经允许严禁转载!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇