P7032 [NWRRC2016]Boys and Girls
前言
感谢 @Umbrella_Leaf 大佬的指导
(看分类过程的可以看有灰色竖线的部分)
题解
设与男孩相邻的数量为
首先可以凭感觉知道:这种既带构造又带环的题目,大多都要分类讨论。为了使我们讨论的顺畅一点,首先我们先特判一些特殊情况,创造出一些性质:
当
a=0 时,注意到此时不能填B,必须全填G。容易算出此时的b 的值为n 。因此当且仅当b=n 时有解。b=0 的情况同理。
那么此时满足答案里面至少有一个 G,至少有一个 B。分别考虑每个字符的贡献,注意到这仅与他两边的字符有关。
那么考虑把一段连续的字符缩成一个连续段,分别讨论每个连续段的贡献(假设这个连续段的字符都是 B):
-
设这个连续段的长度为
k ,首先可以观察出与这个连续段相邻的连续段的字符都是G -
当
k=1 时,注意到他两边的字符都是G,那么只有对b 有1 的贡献 -
当
k>1 时,连续段两端的字符与G相邻,因此对b 有2 的贡献;连续段里的所有字符都与B相邻,对a 有k 的贡献。 -
注意连续段个数必须为偶数,若为奇数的话一定会有两个字符相同的连续段相邻,就会不合法。
由于上面的分类贡献比较复杂,因此考虑对于总数(
因此若有
因此
2|t,t\ge 0 ,所以若a+b<n 或2\nmid a+b-n 就无解。
现在,我们知道了
然后似乎很难讨论,那么我们尝试挖掘
-
因为总人数小于等于
n ,因此0\le a,b \le n 。 -
有
n-b\le 0,n-a\le 0 ,即\frac{a+(n-b)}{2}=t \le \frac{a}{2} ,变形一下有:2\times t\le a 。 -
同理,有
2\times t \le b
但这样还不够,一个常见的构造想法是先构造出一个基础解,再在上面进行增添。那么一个朴素的思路就是往联通块里面插数。那么手模一下,有:
- 当一个连续段的个数大于
1 时,假设字符为B。往里面插一个相同的字符,只会使得a 的值增加1 。
那么我们可以首先创建
注意到
当 B),另一个字符分到 G)。因为 G)。那么我们现在对于
考虑
那么考虑
因此
当
t>1,2\mid t 时,初始设置t 个长度为2 的连续段,其中\frac{t}{2} 个连续段的颜色为B,\frac{t}{2} 个连续段的字符为G。那么对于a,b 的贡献已经为2t 其次我们往其中一个字符为
B的连续段放入a-2t 个字符,往其中一个字符为G的连续短放入b-2t 个字符。当
t>1,2\nmid t 时,若a=b=2t ,不合法,否则设a>b ,设置长度为2 的连续段t 个,\frac{t+1}{2} 个连续段的字符为B,\frac{t-1}{2} 个连续段的字符为G。再增加一个长度为1 的连续段,字符为G。然后分给其中一个字符为B的连续段a-2t-1 个字符,分给其中一个字符为G的连续短b-2t 个字符。
然后分类讨论
当
t=0 时,那么每个连续段的长度为1 ,因此若n 为奇数,则至少为出现一个长度为2 的连续段,无解。那么n 为偶数,分配了\frac{n}{2} 个B和G。易得a=b=\frac{n}{2} 时合法,否则不合法。当
t=1 时,设一共有2x 个连续段。其中x 个连续段的字符为B,另外的为G。假设a> b ,那么抽出其中一个字符为B的连续段,加入一个字符。那么此时对a 的贡献为x+2 ,对b 的贡献为x+1 。由于a> b= x+1 ,有a\ge x+2 。因此合法。当a<b 时同理。当
a=b 时,由于上面的构造是唯一解,所以不合法。(注意数据没有这个情况)
然后这题就做完了 QAQ。
代码
注意一下优先级:
1?putchar('B'),putchar('B'):putchar('G'),putchar('G')
的输出为 BBG。
#include<bits/stdc++.h>
#define pc putchar
using namespace std;
const int N=1e5+1e3;
int n,a,b;
void print(){puts("Impossible");exit(0);}
int main()
{
scanf("%d%d%d",&n,&a,&b);
// a 个孩子站在一个男孩旁边
// b 个孩子站在一个女孩旁边
if(a+b<n||((a+b-n)&1))print();
if(a==0||b==0)
{
if(a+b!=n)print();
if(a==0)for(int i=1;i<=n;i++)pc('G');
if(b==0)for(int i=1;i<=n;i++)pc('B');
exit(0);
}
int t=a+b-n>>1;
if(t==0)
{
if(n%2||a!=n/2||b!=n/2)print();
for(int i=1;i<=n;i++)pc(i&1?'G':'B');
exit(0);
}
if(t==1)
{
if(a==b)print();
if(a>b)
{
for(int i=1;i<=a-b+1;i++)pc('B');
for(int i=1;i<=n-(a-b+1);i++)pc(i&1?'G':'B');
}
if(a<b)
{
for(int i=1;i<=b-a+1;i++)pc('G');
for(int i=1;i<=n-(b-a+1);i++)pc(i&1?'B':'G');
}
exit(0);
}
if(t%2==0)
{
a-=2*t;b-=2*t;
for(int i=1;i<=a+2;i++)pc('B');
for(int i=1;i<=b+2;i++)pc('G');
for(int i=1;i<=t-2;i++)pc(i&1?'B':'G'),pc(i&1?'B':'G');
exit(0);
}
a-=2*t;b-=2*t;
if(!a&&!b)print();
if(a)
{
for(int i=1;i<=a+1;i++)pc('B');
for(int i=1;i<=b+2;i++)pc('G');
for(int i=1;i<=t-2;i++)pc(i&1?'B':'G'),pc(i&1?'B':'G');
pc('G');
exit(0);
}
if(b)
{
for(int i=1;i<=b+1;i++)pc('G');
for(int i=1;i<=a+2;i++)pc('B');
for(int i=1;i<=t-2;i++)pc(i&1?'G':'B'),pc(i&1?'G':'B');
pc('B');
exit(0);
}
return 0;
}
后记
在写题时,突然想起了 P6892 [ICPC2014 WF]Baggage。虽然说两个构造方法不同,但是分类讨论的痛苦是相同的。