Codeforces Round #540 (Div. 3)

比赛状态奇差无比 (solved 2/8),CD两题疯狂WA。

A.Water Buying

大水题,pass

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const ll IINF = 9223372036854775807;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;

inline void read(ll &x) {
    ll f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

ll q,b,a,n;
double x,y;

int main() {
	read(q);
	while(q--) {
		read(n), read(a), read(b);
		x = a, y = b/2;
		if(x<=y) {
			cout<<a*n<<endl;
		}else {
			if(n&1)
				cout<<b*(n/2)+a<<endl;
			else
				cout<<b*(n/2)<<endl;
		}
	}
	return 0;
}

B.Tanya and Candies

题意

给定一个序列,删掉任意一个数,要求序列中编号为奇数的和编号为偶数的各自的和相等。问有多少种删法。

题解

找一下规律,并不难发现可以分删除的是第奇数个和偶数个两种情况处理。

代码

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const ll IINF = 9223372036854775807;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;

inline void read(ll &x) {
    ll f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

ll n,a[maxn],x[maxn],y[maxn],f,g,ans;

int main() {
	read(n);
	for(int i=1;i<=n;i++) {
		read(a[i]);
		if(i&1) x[i] = x[i-1] + a[i], y[i] = y[i-1];
		else y[i] = y[i-1] + a[i], x[i] = x[i-1];
	}
	for(int i=1;i<=n;i++) {
		if(i&1) {
			f = x[i-1] + y[n] - y[i];
			g = x[n] - x[i] + y[i];
		}else {
			f = x[i] + y[n] - y[i];
			g = y[i-1] + x[n] - x[i];
		}
		if(f==g) ans++; 
	}
	cout<<ans<<endl;
	return 0;
}

C.Palindromic Matrix

题意

给定一个n×n的矩阵,如果该矩阵的所有列向量和行向量都左右对称,输出YES,否则输出NO。

题解

首先假如n是一个偶数,那么矩阵中的任意一个数至少出现4次,只要把大矩阵分成上下左右四块,并将所有的数填进左上角小矩阵,再对称变换到其他三个即可;
然后考虑假如n是奇数的情况,还是分成4块,那么中间会出现十字交叉的两条竖线,所以就允许只出现1次/2次/3次的数存在,这就要特别考虑一下(导致代码丑陋)。
比赛时由于考虑出错导致wa了。

代码

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const ll IINF = 9223372036854775807;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 20005;
const int N = 105;

inline void read(int &x) {
	int f=1;
	x=0;
	char s=getchar();
	while(s<'0'||s>'9') {
		if(s=='-')f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9') {
		x=x*10+s-'0';
		s=getchar();
	}
	x*=f;
}

int n,a[maxn],cnt1,cnt2,cnt3,cnt4,p[55][55],h;
map<int,int> mp;
bool flag = 1;
vector<int> e,f;

int main() {
	read(n);
	for(int i=0; i<n*n; i++) {
		read(a[i]);
		mp[a[i]]++;
	}
	if(n&1) {
		for(auto i:mp) {
			if(i.second%4==0) cnt1++;
			else if(i.second%4==1) cnt2++;
			else if(i.second%4==2) cnt3++;
			else if(i.second%4==3) cnt4++;
			else flag = 0;
		}
		if(flag) {
			if(cnt2+cnt4!=1 || cnt3>=n) flag = 0;
			else {
				for(auto i:mp) {
					if(i.second%4==1 || i.second%4==3) {
						h = i.first;
					}
					int k = i.second/4;
					for(int j=0; j<k; j++) e.push_back(i.first);
				}
				for(auto i:mp) {
					if(i.second%4==2) {
						f.push_back(i.first);
					}
					if(i.second%4==3) {
						f.push_back(i.first);
					}
				}
				int k = 0;
				for(int i=1; i<=n/2; i++) {
					for(int j=1; j<=n/2; j++) {
						p[i][j] = p[n-i+1][j] = p[i][n-j+1] = p[n-i+1][n-j+1] = e[k++];
					}
				}
				int kk = 0;
				for(int i=1; i<=n/2; i++) {
					if(k<e.size())
						p[n/2+1][i] = p[n/2+1][n-i+1] = p[n-i+1][n/2+1] = p[i][n/2+1] = e[k++];
					else {
						p[n/2+1][i] = p[n/2+1][n-i+1] = f[kk++];
						p[n-i+1][n/2+1] = p[i][n/2+1] = f[kk++];
					}
				}
				p[n/2+1][n/2+1] = h;
				cout<<"YES"<<endl;
				for(int i=1; i<=n; i++) {
					for(int j=1; j<=n; j++) {
						cout<<p[i][j]<<' ';
					}
					cout<<endl;
				}
				return 0;
			}
		}
	} else {
		for(auto i:mp) {
			if(i.second%4==0) cnt1++;
			else flag = 0;
		}
		if(flag) {
			for(auto i:mp) {
				int k = i.second/4;
				for(int j=0; j<k; j++) e.push_back(i.first);
			}
			int k = 0;
			for(int i=1; i<=n/2; i++) {
				for(int j=1; j<=n/2; j++) {
					p[i][j] = p[n-i+1][j] = p[i][n-j+1] = p[n-i+1][n-j+1] = e[k++];
				}
			}
			cout<<"YES"<<endl;
			for(int i=1; i<=n; i++) {
				for(int j=1; j<=n; j++) {
					cout<<p[i][j]<<' ';
				}
				cout<<endl;
			}
			return 0;
		}
	}
	cout<<"NO"<<endl;
	return 0;
}


D.Coffee and Coursework

题意

总共有n杯咖啡,喝第i杯咖啡可以看 a i a_{i} ai页书,总共有m页。
需要注意的是:同一天喝的第二杯咖啡,只能看 a i − 1 a_{i}-1 ai1页,递推下去,第k杯只能看 m a x ( a i k − k + 1 , 0 ) max(a_{i_{k}}-k+1,0) max(aikk+1,0)页。
问最少多少天能看完。

题解

不难看出,这是一个二分题。所以首先二分一个天数,然后判断能否在这么多天看完即可。
难点在于,二分出来后如何合理规划每天喝哪几杯咖啡。也很显然(比赛时并没有看出来),第1天和第1大的,第二天和第2大的,类推下去,然后第k+1大的还是第一天喝,这样就最大限度的利用了咖啡。
res += max(0,a[i]-i/x); 关键就是这一行,恰好处理掉了每天应该扣掉的咖啡,非常巧妙。

代码

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const ll IINF = 9223372036854775807;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int n,m,a[maxn],sum;

bool check(int x) {
	 int res = 0;
	 for(int i=0;i<n;i++) {
	 	res += max(0,a[i]-i/x); 
	 }
	 return res>=m;
}

int main() {
	read(n), read(m);
	for(int i=0;i<n;i++) {
		read(a[i]);
		sum += a[i];
	}
	sort(a,a+n,greater<int>());
	if(sum<m) {
		cout<<-1<<endl;
		return 0;
	}
	int l=0,r=n;
	while(l<r-1) {
		int mid = (l+r) / 2;
		if(check(mid)) r = mid;
		else l = mid;
	}
	cout<<r<<endl;
	return 0;
}


F.Tree Cutting (Easy Version)

题目

一个树有n个节点,每个节点可能是蓝色、红色或者无色。要求断掉树中的任意一条边,使得分成的两部分任意一部分都不同时包含蓝色和红色节点,问总共有多少条边可以这么断掉。

题解

两部分不同时含有红色和蓝色节点,就意味着某一部分含有所有的红色节点和蓝色节点。
所以先遍历这棵树,如果某个节点的子树含有所有的红色节点或蓝色节点(不同时),那么ans++。

代码

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 300005;
const int N = 105;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int n,a[maxn],ans,red,blue,vis[maxn];
vector<int> e[maxn];

pair<int,int> dfs(int x,int p) {
	int r = (a[x]==1), b = (a[x]==2);
	vis[x] = 1;
	for(int i=0;i<e[x].size();i++) {
		if(!vis[e[x][i]] && p!=e[x][i]) {
			pair<int,int> q = dfs(e[x][i],x);
			if(q.first==red && q.second==0) ans++;
			if(q.first==0 && q.second==blue) ans++;
			r += q.first;
			b += q.second;
		}
	}
	return {r,b};
}

int main() {
	read(n);
	for(int i=1;i<=n;i++) {
		read(a[i]);
		if(a[i]==1) red++;
		else if(a[i]==2) blue++;
	}
	for(int i=1;i<n;i++) {
		int x,y;
		read(x), read(y);
		e[x].push_back(y);
		e[y].push_back(x);  
	}
	dfs(1,-1);
	cout << ans << endl;
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总想玩世不恭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值