比赛状态奇差无比 (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
ai−1页,递推下去,第k杯只能看
m
a
x
(
a
i
k
−
k
+
1
,
0
)
max(a_{i_{k}}-k+1,0)
max(aik−k+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;
}