本场链接:Codeforces Global Round 14
看不懂E,F不知道什么时候能补出来.
题目大意:给定\(n,x\),以及一个数组\(w\).要求重排\(w\),使得不存在某个\(j\)满足\(\sum\limits_{i = 1}^j w_i = x\),即不存在一个前缀和累加起来得到\(x\).保证元素互不相同.
首先特判掉一种情况:整个数组的和就是\(x\),显然无解.
如果\(w\)本来就没有一个前缀和\(=x\)那么直接输出就完事了,但是如果存在呢?因为每两个元素互不相同,如果\(i\)位置的前缀和与\(x\)相等,那么将\(w_i\)与任何一个\(j > i\)的\(w_j\)交换之后必然能使这个前缀和不等于\(x\).为了避免之后还会出现\(x\),不妨将整个数组排序,那么这样前缀和就是单调的,直接swap
破坏了性质的位置和下一个位置的元素即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 105;
int w[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,x;scanf("%d%d",&n,&x);
int sum = 0;
forn(i,1,n) scanf("%d",&w[i]),sum += w[i];
if(sum == x)
{
puts("NO");
continue;
}
sort(w + 1,w + n + 1);
int cur = 0;
forn(i,1,n - 1)
{
cur += w[i];
if(cur == x)
{
swap(w[i],w[i + 1]);
break;
}
}
puts("YES");
forn(i,1,n) printf("%d ",w[i]);
puts("");
}
return 0;
}
给定\(n\)个等腰直角三角形,问能不能把他们拼成一个正方形.不能相互覆盖,必须全部使用.
如果把边长定义为\(1\),不难找到最基本的两种构造是长度为\(1\)的正方形和长度为\(\sqrt(2)\)的正方形,其余的情况不过是这两种堆叠而成的.两者需要的三角形个数分别是\(2\),\(4\).对于一个有\(i*i\)个基本元素(前面两种)的正方形,需要的个数就是\(i^2*2\)或者\(i^2*4\).所以只需要检查\(n/2\)或者\(n/4\)是否是平方数即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
if(n == 1)
{
puts("NO");
continue;
}
bool ok = 0;
int n2 = n / 2,sqn2 = sqrt(n2);
if(n % 2 == 0 && 1ll * sqn2 * sqn2 == n2) ok = 1;
int n4 = n / 4,sqn4 = sqrt(n4);
if(n % 4 == 0 && 1ll * sqn4 * sqn4 == n4) ok = 1;
if(ok) puts("YES");
else puts("NO");
}
return 0;
}
题目大意:有\(n\)个砖,每个高度为\(h_i\),要求拿这些砖头堆出\(m\)座塔,每个塔的高度就是使用的砖块高度之和.不能有一个塔的高度为\(0\),每个砖块必须使用.构造一个任意两座塔的高度之差都不严格超过\(x\)的方案.
不难想到一个更强的目标:使最大的高度之差最小,这看起来像是一个二分答案,但是实现check
摆脱不了\(O(n^2)\)的枚举,考虑直接贪心:对于每个元素,将他放在当前高度最低的塔中,这样构造的高度是最平均的.因为如果交换两个元素,等价于选择的时候某个元素就没有选入当前高度最小的塔,这样势必会让差距变大.
代码里的做法要更拐弯一些:因为要保证所有塔高度平均,所以在第一轮分配的时候首先把所有砖块尽量往高度之和的平均值去凑.第二轮的时候就没得选了直接放.没什么具体差别.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 1e5+7;
int h[N],ans[N];
bool st[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,m,x;scanf("%d%d%d",&n,&m,&x);
int allsum = 0;
forn(i,1,n) scanf("%d",&h[i]),allsum += h[i],st[i] = 0;
int target = allsum / m;
priority_queue<pii,vector<pii>,greater<pii>> q;
forn(i,1,m) q.push({0,i});
forn(i,1,n)
{
auto _ = q.top();q.pop();
if(_.x + h[i] <= target)
{
_.x += h[i];
st[i] = 1;
ans[i] = _.y;
}
q.push(_);
}
forn(i,1,n)
{
if(st[i]) continue;
auto _ = q.top();q.pop();
_.x += h[i];
st[i] = 1;
ans[i] = _.y;
q.push(_);
}
vector<int> ans_c;
while(!q.empty()) ans_c.push_back(q.top().x),q.pop();
sort(ans_c.begin(),ans_c.end());
if(ans_c.back() - ans_c[0] > x) puts("NO");
else
{
puts("YES");
forn(i,1,n) printf("%d ",ans[i]);
puts("");
}
}
return 0;
}
一共有\(n\)条袜子,保证\(n\)是偶数.每个袜子有自己的方向和颜色,一对袜子是匹配的当且仅当颜色相同且一左一右.你可以花\(1\)块钱使袜子的颜色变成指定颜色,或是让他的方向变换一次.求最少花费多少能使\(n\)条袜子各自匹配.
另外一种情况对称处理即可.
注意不要一边遍历一边删除.可能会引发迭代器失效.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 2e5+7;
int c[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n,l,r;scanf("%d%d%d",&n,&l,&r);
forn(i,1,n) scanf("%d",&c[i]);
map<int,int> L,R;
forn(i,1,l) ++L[c[i]];
forn(i,l + 1,n) ++R[c[i]];
for(auto& _ : L)
{
int v = _.x;
if(!R.count(v)) continue;
int cost = min(_.y,R[v]);
L[v] -= cost;R[v] -= cost;
}
int szL = 0,szR = 0,res = 0;
for(auto& _ : L) szL += _.y;
for(auto& _ : R) szR += _.y;
if(szL != szR)
{
if(szL < szR)
{
for(auto& _ : R)
{
if(_.y == 1) continue;
while(_.y >= 2 && szL < szR)
{
szR -= 2;
_.y -= 2;
++res;
}
}
}
else
{
for(auto& _ : L)
{
if(_.y == 1) continue;
while(_.y >= 2 && szR < szL)
{
szL -= 2;
_.y -= 2;
++res;
}
}
}
}
if(szL < szR) res += szR;
else if(szL > szR) res += szL;
else res += szL;
printf("%d\n",res);
}
return 0;
}
题目大意:有\(n\)个机子,你可以手动开某些机子.这些机子比较牛逼,当位于\(i-1\)和\(i+1\)的机子同时开启时,位于\(i\)的机子会自动启动.求:手动开启机子位置序列的方案数,要求所有机子最后都开启.
如果从左到右去看整个操作序列,那么就可以这么看:先手动开了\(1,2,3...i_1 - 1\)的机子,之后\(i_1\)的机子自动开启.再往下就是\(i_1+1...i_2 - 1\)的机子手动去开,\(i_2\)的机子自动打开.按每个自动开启的机子分段.
考虑DP:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 405;
int C[N][N],f[N][N],pw2[N];
int n,MOD;
void init()
{
pw2[0] = 1;
forn(i,1,N - 1) pw2[i] = 2ll * pw2[i - 1] % MOD;
forn(i,0,N - 1) forn(j,0,i)
{
if(!j) C[i][j] = 1;
else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
}
int main()
{
scanf("%d%d",&n,&MOD);
init();
f[0][0] = 1;
forn(i,0,n - 1) forn(j,0,i) forn(k,1,n - i)
f[i + k + 1][j + k] = (f[i + k + 1][j + k] + 1ll * f[i][j] * C[j + k][k] % MOD * pw2[k - 1] % MOD) % MOD;
int res = 0;
forn(i,0,n) res = (res + f[n + 1][i]) % MOD;
printf("%d\n",res);
return 0;
}
Codeforces Global Round 14 A~E题解
原文:https://www.cnblogs.com/HotPants/p/14727332.html