【CF】Codeforces Round #540 (Div. 3)(C-F)部分题解

Codeforces Round #540 (Div. 3)

【小结】:

比赛时只完成了3个题,但是杨哥哥已经完成了7个题,而且有些题补题补得我脑阔疼,终于发现差距在哪里了,差距就是你在刷题的时候别人也在刷题,你在玩的时候别人也在刷题,这个差距不可收缩吗?自己要提一下效率上来不然就慢别人好几步了。


C题:构造题:

1118C - Palindromic Matrix

题目是,给n×n个数字,然后让你构造出,N维回文矩阵出来。

【题解】:

分奇偶性情况讨论:

首先比较容易解决的是偶数情况,因为发现偶数上下均需要对称,说明:每一个位置都需要4个,所以所有数字都必须是4的倍数,不然就无法构造,然后只要对于左上角填上去,排序后每隔4个来填。

然后奇数情况:

你发现奇数是在原来的基础上发生了变化,多了中间的一行和一列。

注意的细节:

1、必须要有(n/2)*(n/2)个   4的倍数。首先填补,主要是(n/2)*(n/2)不要少了括号,不然左结合你就错了一遍了。

2、如果填补完后,中间的缝隙,用2的倍数来填补,所以说,之前用4 的倍数如果有剩下来的必须转化一下。

个人代码比较长:

#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=500;
const int M=25;
int a[N];
int vis[N<<2];
int b[N][N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n*n;i++){
        scanf("%d",&a[i]);
        vis[a[i]]++;
    }

    sort(a,a+n*n);
    if(n%2==0){
        int f=1;
        for(int i=1;i<=1000;i++){
            if(vis[i]%4) f=0;
        }
        if(f){
            printf("YES\n");
            int cur=0;
            for(int i=0;i<n/2;i++){
                for(int j=0;j<n/2;j++,cur+=4){
                    b[i][j]=b[i][n-1-j]=b[n-1-i][j]=b[n-1-i][n-1-j]=a[cur];
                }
            }
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                    printf("%d%c",b[i][j],j==n-1?'\n':' ');
                }
            }
        }else{
            puts("NO");
        }
    }
    else{
        int tot=0,f=0,cnt=0,t1=0,t2=0;
        int c[2][1005];
        for(int i=1;i<=1000;i++){
            if(vis[i]&1) {tot++;f=i;}
            if(vis[i]>=4){
                int t=vis[i]/4;
                cnt+=t;
                vis[i]-=4*t;
                while(t--){
                    c[0][t1++]=i;
                }
            }
            if(vis[i]>=2){
                c[1][t2++]=i;
            }
        }
        if(tot>1||(n/2)*(n/2)>cnt){
            //printf("%d %d %d\n",tot,(n/2)*(n/2),cnt);
            printf("NO\n");
        }else{
            printf("YES\n");
            b[n/2][n/2]=f;
            int cur=0;
            //for(int i=0;i<t1;i++) printf("%d%c",c[0][i],i==t1-1?'\n':' ');
            //for(int i=0;i<t2;i++) printf("%d%c",c[1][i],i==t2-1?'\n':' ');
            for(int i=0;i<n/2;i++){
                for(int j=0;j<n/2;j++,cur++){
                    b[i][j]=b[i][n-1-j]=b[n-1-i][j]=b[n-1-i][n-1-j]=c[0][cur];
                }
            }

            while(n-1>t2&&cur<=t1){
                c[1][t2]=c[1][t2+1]=c[0][cur];
                t2+=2;
                cur++;
            }
            //for(int i=0;i<t2;i++) printf("%d%c",c[1][i],i==t2-1?'\n':' ');
            cur=0;
            for(int i=0;i<n/2;i++,cur++){
                b[i][n/2]=b[n-1-i][n/2]=c[1][cur];
            }
            for(int j=0;j<n/2;j++,cur++){
                b[n/2][j]=b[n/2][n-1-j]=c[1][cur];
            }
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                    printf("%d%c",b[i][j],j==n-1?'\n':' ');
                }
            }
        }
    }
    return 0;
}
/*
5
1 3 5 3 1
2 4 6 4 2
7 8 9 8 7
2 4 6 4 2
1 3 5 3 1
 */

1118D2 - Coffee and Coursework (Hard Version)

【题意】:

给你n杯咖啡,然后可以有ai的动力写稿子ai页,然后有m页稿子,如果连续和咖啡,第二杯页数就会-1,然后第三杯就会-2/、如此类推,然后问你最少多少天可以把稿子赶出来。

【题解】:

赛后一直以为是分组来写,后来发现如果出现很多咖啡很小的时候这个就不可行了。然后看到别人的做法都是二分写的,你就明白为什么二分了,二分天数,每杯咖啡一直放,按从大到小一直放,因为可能出现 放到后面 可能会出现负数,你就判断一下,最后的总和>=m true.然后看看题解的写法。

题解的写法:

#include <bits/stdc++.h>

using namespace std;

int n, m;
vector<int> a;

bool can(int i) {
	long long sum = 0; // there can be a bug
	for (int j = 0; j < n; ++j) {
		sum += max(a[j] - j / i, 0);
	}
	return sum >= m;
}

int main() {
#ifdef _DEBUG
	freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);
#endif
	
	cin >> n >> m;
	
	a = vector<int>(n);
	for (int i = 0; i < n; ++i) {
		cin >> a[i];
	}
	sort(a.rbegin(), a.rend());
	
	int l = 1, r = n;
	while (r - l > 1) {
		int mid = (l + r) >> 1;
		if (can(mid)) r = mid;
		else l = mid;
	}
	
	if (can(l)) cout << l << endl;
	else if (can(r)) cout << r << endl;
	else cout << -1 << endl;
	
	return 0;
}

自己参考别人的写法:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const int inf=0x3f3f3f3f;
ll n,m,a[N];
bool check(ll mid){
    ll t=0;
    for(int i=0;i<n;i++){
        t=t+max(0ll,(a[i]-i/mid));
    }
    return t>=m?true:false;
}
ll cmp(ll a,ll b){
    return a>b;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%lld",&a[i]);
    }
    sort(a,a+n,cmp);
    ll L=1,R=n+1,mid,ans=-1;
    while(L<=R){
        mid=(L+R)>>1;
        if(check(mid)){
            ans=mid;
            R=mid-1;
        }else{
            L=mid+1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 


1118E - Yet Another Ball Problem

【题意】:

题意为:让你构造出n对出来,然后每一对中数字必须是[ 1, k ],然后最后实现的n对出来:

注意每一对有要求:

1、不存在两对完全相同,即(ai,bi)!=(aj,bj) (i!=j)

2、不存在 一对里相同,即(ai,bi) (ai!=bi)

3、相邻之间不能出现连续的第一个相同或这第二个相同 。

即(1,2) (1,×)    ,或者(1,2)(×,2)


官方题解是第一维都是1-K,第二维有意错开即可.

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef _DEBUG
	freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);
#endif
	
	int n, k;
	cin >> n >> k;
	
	if (n > k * 1ll * (k - 1)) {
		cout << "NO" << endl;
	} else {
		cout << "YES" << endl;
		int cur = 0;
		for (int i = 0; i < n; ++i) {
			cur += (i % k == 0);
			cout << i % k + 1 << " " << (cur + i % k) % k + 1 << endl;
		}
	}
	
	return 0;
}

成浩哥的做法:

就是跑一个矩阵,这个矩阵就是一直跑反向斜对角线。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll n,k;
    scanf("%lld%lld",&n,&k);
    if(n>(k-1)*k){
        printf("NO\n");
    }else{
        printf("YES\n");
        ll x=1,y=1;
        for(ll i=1;i<=n;i++){

            x++;y--;
            if(x==y){
                i--;continue;
            }
            if(y==0){
                y=x;x=1;
            }
            if(y>k||x>k){
                i--;continue;
            }
            printf("%lld %lld\n",x,y);
        }
    }
    return 0;
}


1118F1 - Tree Cutting (Easy Version)

【题意】:

给你一棵树,然后每一个节点都有相应的颜色,分别为无色,红色,蓝色,问你切断哪里可以把树分为红色和蓝色两个部分。

【官方做法】:

用DFS搜索,然后把子树的情况相加,只是统计  全部红色都有 而没有蓝色,和全部蓝色没有红色的情况。

#include <bits/stdc++.h>

#define forn(i, n) for (int i = 0; i < int(n); i++)

using namespace std;

const int N = 300 * 1000 + 13;

int n;
int a[N];
vector<int> g[N];
int red, blue;
int ans;

pair<int, int> dfs(int v, int p = -1){
	int r = (a[v] == 1), b = (a[v] == 2);
	for (auto u : g[v]) if (u != p){
		auto tmp = dfs(u, v);
		ans += (tmp.first == red && tmp.second == 0);
		ans += (tmp.first == 0 && tmp.second == blue);
		r += tmp.first;
		b += tmp.second;
	}
	return make_pair(r, b);
}

int main() {
	int n;
	scanf("%d", &n);
	
	forn(i, n){
		scanf("%d", &a[i]);
		red += (a[i] == 1);
		blue += (a[i] == 2);
	}
	
	forn(i, n - 1){
		int v, u;
		scanf("%d%d", &v, &u);
		--v, --u;
		g[v].push_back(u);
		g[u].push_back(v);
	}
	
	ans = 0;
	dfs(0);
	printf("%d\n", ans);
	return 0;
}

【杨哥哥做法】:

我发现杨哥哥真的强,强在哪里呢,可以把问题剖析得很好,用第一个dfs把全部颜色的情况往根节点进行统计,然后用dfs2来历遍所有树干,看一下改变断了是否可以把所有的红蓝分开。 

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
vector<int>G[N];
int sum[N][3];
int a[N],n,ans=0;
void dfs1(int u,int fa){
    sum[u][a[u]]++;
    for(auto v:G[u]){
        if(v==fa) continue;
        dfs1(v,u);
        for(int j=0;j<3;j++){
            sum[u][j]+=sum[v][j];
        }
    }
}
void dfs2(int u,int fa){
    for(auto v:G[u]){
        if(v==fa) continue;
        dfs2(v,u);
        if(sum[v][1]==0&&sum[v][2]==sum[1][2]){
            ans++;
        }else if(sum[v][2]==0&&sum[v][1]==sum[1][1]){
            ans++;
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1,u,v;i<n;i++){
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,-1);
    dfs2(1,-1);
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/Z_sea/article/details/87862378