A. Dual Trigger
这道题只能将灯泡从0变为1,所以我们观察最终状态s的1的位置,可以看出:
1.1的个数为奇数不合法,输出NO;
2.1的个数为2且相连,输出NO;
3.其他情况均为YES。
void solve()
{
string s; int n,i;
cin>>n>>s; s="."+s;
vector<int>vec;//将所有为1的下标存储方便特判
for(i=1;i<=n;i++)
if(s[i]=='1') vec.push_back(i);
if(vec.size()%2) cout<<"NO\n";
else
{
if(vec.size()==2)
{
if(vec[0]==vec[1]-1) cout<<"NO\n";
else cout<<"YES\n";
}
else cout<<"YES\n";
}
}
B. Battle Cows
其实就是要每场比赛都要下一个人与上一场的获胜者进行较量,如果第k个人前面有比他大的,则当前方案胜场为0。所以我们要让第k个人尽量靠前或将比k大的第一个人向后移,即求出第k个人和第一个人交换和第k个人和第一个比它大的人交换的方案取更优的即可。
int a[N];
void solve()
{
int n,k,i; cin>>n>>k;
for(i=1;i<=n;i++) cin>>a[i];
swap(a[k],a[1]);
int ans1=0; //与第一个人交换的方案的胜场数
for(i=2;i<=n;i++)
{
if(a[i]>a[1]) break;
else ans1++;
}
swap(a[k],a[1]); //swap回来进行下一个方案的求解
int flag=n+1,ans2=0; //flag为第一个比k大的人的下标
for(i=1;i<=n;i++)
if(a[i]>a[k])
{
flag=i;
break;
}
if(flag<k)
{
swap(a[k],a[flag]);
if(flag>1) ans2++;//特判当前k是不是在开头,否则会赢前面的人一场
for(i=flag+1;i<=n;i++)
{
if(a[i]>a[flag]) break;
else ans2++;
}
}
cout<<max(ans1,ans2)<<"\n";
}
C. Ticket Hoarding
该题可以看出,若是我们确保要买一些天的票,我们会尽可能的购买至上限而不是这些天平均分配(会导致买到价格提高的票更多),所以对于买k张门票,我们会有一个这样的购买数量序列{m,m...m,m,x(x=k%m)}(加和为k)(可以交换位置),我们可以枚举这些天哪天购买了x个即可。
对于怎样确定这些天,我们可以贪心的将最小的前几天选出来进行操作。
#define int long long
#define PII pair<int,int>
#define fir first
#define sec second
const int inff=0x3f3f3f3f3f3f3f3f
PII a[N]; //一维存价格,二维存下标
bool cmp(PII x,PII y) {return x.sec<y.sec;}
void solve()
{
int n,m,k,i; cin>>n>>m>>k;
//注意要用long long,或者宏定义int
for(i=1;i<=n;i++)
{
int x,y=i; cin>>x;
a[i]={x,y};
}
sort(a+1,a+1+n); //按照大小排序
int d=(m+k-1)/m,x=k-(d-1)*m; //d为需要枚举的天数
sort(a+1,a+1+d,cmp); //前d个必选,按照下标排序
int sum=0,ans=inff;
for(i=1;i<=d;i++) sum+=(a[i].fir+(i-1)*m)*m; //每个都选m个,接下来枚举哪个是x即可
for(i=1;i<=d;i++)
{
int pr=(i-1)*m,nex=(d-i)*m;
//pr为前面购买的个数,nex为后续购买的个数
int cnt=sum-(a[i].fir+pr)*m+(a[i].fir+pr)*x;//当前这位从购买m个变为x个修改的价格
int sub=m-x; //后续购买每个物品降低的价格
cnt-=sub*nex;
ans=min(ans,cnt);
}
cout<<ans<<"\n";
}
需要注意的是,其实我们不去枚举哪个是x也可以,排序后的选择并不影响最终结果,可以直接按照价格从小到大贪心的买到上限即可,以下是证明过程:
D. Buying Jewels
这道题有以下两种显而易见的情况:
1.k>n,无解
2.n%k=0,输出一个价格n/k即可
在其他情况,我们可以尝试构造出买了一个珠宝后剩下的余数恰好可以购买一元一个的珠宝,所以我们令下一步的操作使k-1且n=k,若能构成这一步则输出两堆答案即可,否则可以证明无解(购买个数不足),证明过程如下:
void solve()
{
int n,k; cin>>n>>k;
if(k>n) { cout<<"NO\n"; return ; }
if(n%k==0) { cout<<"YES\n1\n"<<n/k<<"\n"; return ; }
int next=k-1; //令下一步n和k都为next
if(n/(n-next)>1) { cout<<"NO\n"; return ; }
//该情况构造不出n=k=next的情况
cout<<"YES\n2\n"<<n-next<<" "<<"1\n";
}
E. No Palindromes
第二行的重码是回文串的意思。
赛场上捏了几个样例,发现三个非回文串拼接可以构造成两个非回文串。
例如:{ab,ba,ab}可以变为{abb,aab}且我没有找到反例,所以我认为最终答案若有解只可能是一整段是非回文串或分成两段非回文串。
题解证明过程如下:
所以只需要特判整个是不是回文串,并且枚举断点,正反跑两遍字符串哈希对比一下即可。
typedef unsigned long long ULL;
const int P = 131;
int n, m;
char s[N],t[N];
ULL h1[N], p1[N];
ULL h2[N], p2[N];
void init1()
{
p1[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h1[i] = h1[i - 1] * P + s[i];
p1[i] = p1[i - 1] * P;
}
}
void init2()
{
p2[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h2[i] = h2[i - 1] * P + t[i];
p2[i] = p2[i - 1] * P;
}
}
ULL get1(int l, int r)
{
return h1[r] - h1[l - 1] * p1[r - l + 1];
}
ULL get2(int l, int r)
{
return h2[r] - h2[l - 1] * p2[r - l + 1];
}
void solve()
{
cin>>s+1; int i; n=strlen(s+1);
for(i=1;i<=n;i++) t[i]=s[n-i+1];
init1(); init2();
if(get1(1,n)!=get2(1,n))
{
cout<<"YES\n1\n";
cout<<s+1<<"\n";
return ;
}
for(i=2;i<n-1;i++)
{
int l1=1,r1=i;
int l2=i+1,r2=n;
if(get1(l1,r1)!=get2(n-i+1,n)&&get1(l2,r2)!=get2(1ll,n-i))
{
cout<<"YES\n2\n";
for(int j=1;j<=i;j++) cout<<s[j]; cout<<" ";
for(int j=i+1;j<=n;j++) cout<<s[j]; cout<<"\n";
return ;
}
}
cout<<"NO\n";
}