Educational Codeforces Round 164 (Rated for Div. 2) - VP记录
Preface
对着 D 题发了一个多小时的呆,我真的服了。
下次应当考虑各个角度,各个方向,绿题难度的题(特别是 CF 上的)一般思维深度都不会非常高,反而更考验思维广度。
A. Painting the Ribbon
显然 Alice 的最优策略就是将彩带涂成形如 12312312...
的形式。
而 Bob 的最优策略当然是将彩带涂成出现次数最多的颜色。
出现次数最多的颜色最少出现 \(\lceil \frac{n}{m} \rceil\) 次,Bob 最多要涂 \((n - \lceil \frac{n}{m} \rceil)\) 次,将它与 \(k\) 比较即可。
点击查看代码
#include<cstdio>
using namespace std;
int n,m,k;
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
printf("%s\n",n-(n+m-1)/m>k?"YES":"NO");
}
return 0;
}
B. Make It Ugly
每次操作将形如 aba
的结构改成 aaa
,要可以将原数列全部变成一个数,漂亮数列需要是 aababaaabaaba
这样的结构,多个 a
中间间杂单个 b
,且第一个一定是 a
。
如果要移除元素让它不是上述结构,只能移除两个 b
中间的全部 a
来使多个 b
相邻。
所以只需要找最短的连续 a
即可。
点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int n,a[N];
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int len=0,ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
if(a[i]==a[1]) len++;
else
{
ans=min(ans,len);
len=0;
}
}
ans=min(ans,len);
printf("%d\n",ans==n?-1:ans);
}
return 0;
}
C. Long Multiplication
之前似乎做过这道题,不过那时还需要求积,要用高精度。而这个就不用。
简单贪心,让两数大小最接近即可。因为和是一定的,所以要让两数尽量接近平均数。
具体来说,找到不相同的最高位,让这一位大的那个数其它位最小,这一位小的那个数其它位最大。
点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZE=105;
struct Bignum{
int num[SIZE];
int size;
void read()
{
static char str[SIZE];
scanf("%s",str+1);
int len=strlen(str+1);
for(int i=1;i<=len;i++)
num[i]=str[len-i+1]-'0';
size=len;
return;
}
void print()
{
for(int i=size;i>=1;i--)
putchar(num[i]+'0');
putchar('\n');
return;
}
void clear()
{
while(size)
num[size--]=0;
return;
}
}a,b;
bool operator < (const Bignum &x,const Bignum &y)
{
if(x.size!=y.size) return x.size<y.size;
for(int i=x.size;i>=1;i--)
if(x.num[i]!=y.num[i]) return x.num[i]<y.num[i];
return false;
}
int main()
{
int T; scanf("%d",&T);
while(T--)
{
a.read(),b.read();
if(a<b) swap(a,b); //make a>b
bool flag=false;
for(int i=a.size;i>=1;i--)
if(a.num[i]>b.num[i])
{
if(flag) swap(a.num[i],b.num[i]);
else flag=true;
}
a.print(),b.print();
}
return 0;
}
D. Colored Balls
一眼 DP,可惜状态表示错了,应该设出现次数而不是答案的。
设 \(f_k\) 表示总和为 \(k\) 的方案数,这个方案数用背包很容易可以求出来。
每次加数时,如果 \(a_i>k\),那么此时的单个贡献就是 \(a_i\)(至少要给每个 \(a_i\) 都分一个),否则就为 \(\left\lceil \frac{j+a_i}{2} \right\rceil\) 个,因为每两个球占一个组。
然后将单独的贡献乘以出现的次数 \(f_k\) 就是此时的总贡献。
#include<cstdio>
#include<algorithm>
using namespace std;
const int SUM=5000,N=5005,P=998244353;
int n,a[N],f[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
long long ans=0;
f[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=a[i];j++) ans+=1ll*f[j]*a[i]%P;
for(int j=a[i]+1;j<=SUM;j++) ans+=1ll*f[j]*(j+a[i]+1>>1)%P;
ans%=P;
for(int j=SUM;j>=a[i];j--) f[j]+=f[j-a[i]],f[j]%=P;
}
printf("%lld\n",ans);
return 0;
}
E. Chain Reaction
赛后补的。
设 \(f_i\) 表示砍掉所有 \(a_i \in [1,i]\) 以后剩下的连续块数(注意这不是动态规划)。
当每次砍 \(t\) 块的时候,对于所有的 \(i \in [0,\lceil\frac{m}{t}\rceil]\)(\(m\) 为 \(a\) 的最大值),\(t \times i\) 就是每次一共砍掉的高度,而砍完这次后,砍完下一次所需要的次数就是此时的连通块数(因为每个块都要砍)。
因此,答案就是 \(\sum_{i=0}^{\lceil\frac{m}{t}\rceil}f_{t \times i}\)。
\(f\) 可以预处理出来,只需要讨论某一层的某个位置断开以后会不会影响连通块的块数即可。
#include<cstdio>
#include<vector>
using namespace std;
const int N=1e5+5;
int n,a[N],m;
vector<int> h[N];
bool survive[N];
int f[N]; //砍掉第1~i层后剩下的块数
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
m=max(m,a[i]);
h[a[i]].push_back(i);
survive[i]=true;
}
int conn=1;
f[0]=conn;
for(int i=1;i<=m;i++)
{
for(int pos: h[i])
{
if(survive[pos-1]&&survive[pos+1]) conn++; //从pos一分为二
if((!survive[pos-1])&&(!survive[pos+1])) conn--; //pos这一块彻底沉没
survive[pos]=false;
}
f[i]=conn;
}
for(int i=1;i<=m;i++)
{
long long ans=0;
for(int j=0;j<=m;j+=i)
ans+=f[j];
printf("%lld ",ans);
}
return 0;
}
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18511593