签到题,按照题意模拟即可。
对于后手而言,不论先手最后剩下哪个数字,他都会让自己剩下的数字,与先手剩下的数字相差最小。
对于先手而言,因为他也知道,后手会使他剩下的数字与后手剩下的数字相差最小,所以他只能尽量使这个最小的相差最大化。
具体来说,对于a数列中的每个数字,只需枚举b数列中的所有数字,找到相差最小的一对,记它们的差为c,那么所有的c中最大的一个,就是答案。
签到题,只需考虑某一种字母数量超过n/2的情况即可。对于这种情况,当其他字母数量之和>2时,可以构造出题目要求的字符串,否则不能。
给定一个有n个顶点的无向完全图(n为奇数),每一条边有一个边权。题目的要求是将这个图分为多个环(每条边只能且必须使用一次),记环内相邻两条边的边权为u、v,使得所有的max(u,v)之和最小。
直接构造很难下手,于是分析题目条件。
由于n为奇数,不难发现所有点的度数为偶数,而一个环中相邻的两条边又是通过一个公共点相连接的。因此,我们可以将每个点连接的这(n-1)条边分为(n-1)/2对,不必考虑每一对边具体是属于哪一个环的,只需要让每一对边的max值之和最小即可。
那么,我们对每个点连接的边按边权排序,取相邻的两条边构成一对即可,时间复杂度O(n2log n)。
B题的题面比较冗长,大意是你有两个等级,给你n个任务,每个任务在1级和2级下做的时候,花费的时间和得到的经验值是不同的。当1级的经验值满了之后就会升级,升级后只能在2级下做任务。并且1级的经验值溢出后,可以加到2级的经验值上。问最少花费多长时间,才能使1、2级的经验值均满。
显然,这是个01背包问题,分别考虑每个物品对1级和2级的贡献即可。一个细节是需要对读进来的数据按照x(在1级下做任务得到的经验值)从小到大排序,这样可以尽量多的将经验溢出给2级。
设dp[j][k]为:1级经验为j、2级经验为k时,花费的最少时间。受题意影响,lincong在写dp转移方程的时候,误以为只有j>=s1(1级升级所需的经验值)时,才能使用一个任务在2级下做的时间和经验。因此写出了如下错误代码:
for(int i=1;i<=n;i++)//枚举每个任务
for(int j=s1;j>=0;j--)
for(int k=s2;k>=0;k--)
{
int nj=min(s1,j+a[i].x);
int nk1=min(s2,k+max(j+a[i].x-s1,0));//计算溢出
int nk2=min(s2,k+a[i].y);
dp[nj][nk1]=min(dp[nj][nk1],dp[j][k]+a[i].t);//当前任务在1级下做
if(j>=s1) dp[j][nk2]=min(dp[j][nk2],dp[j][k]+a[i].r);//当前任务在2级下做
}
然而事实是,我们可以在任何时候使用一个任务在2级下做的时间和经验。
原因是:在1级经验不满s1时,我们使用一个任务在2级下做的时间和经验,可以理解为把这个任务寄存到2级下,当1级经验足够s1之后,立刻去做这个任务即可。
反而是当j>=s1时,我们不能使用一个任务在1级下做的时间和经验,因为这时已经升级到2级,只能在2级下做任务了。正确代码如下:
for(int i=1;i<=n;i++)//枚举每个任务
for(int j=s1;j>=0;j--)
for(int k=s2;k>=0;k--)
{
int nj=min(s1,j+a[i].x);
int nk1=min(s2,k+max(j+a[i].x-s1,0));//计算溢出
int nk2=min(s2,k+a[i].y);
if(j<s1) dp[nj][nk1]=min(dp[nj][nk1],dp[j][k]+a[i].t);//当前任务在1级下做
dp[j][nk2]=min(dp[j][nk2],dp[j][k]+a[i].r);//当前任务在2级下做(j<s1时可认为是寄存到2级下)
}
因此WA了11发之后成功AC此题。
1mszs补了G题,lincong补了AEF题。
纯模拟。其实VP时,1mszs已经基本想出此题的正确做法了,由于时间不够等不可抗因素,未能AC此题。
G题的题意是这样:给你一个侧面投影和一个正面投影,让你判断这两个投影能否组成一个立体结构。如果能的话,输出组成它的小方块个数的最大值,以及它们的位置;接着输出小方块个数的最小值,以及它们的位置(输出字典序最小的一种方案)。如果不能的话,输出-1。
最大值很好处理,只要是能放小方块的地方,全部放上即可。
最小值也不算难,我们按层去放小方块。首先,我们需要计算出这一层的x轴和y轴上分别有多少个阴影。对于xy数量相同的情况,我们1对1依次去放即可;对于xy数量不等的情况,我们让多出来的部分全部放在第1行(列),剩余的1对1依次去放即可。
VP结束后,1mszs继续完善考场上的代码,WA了3发之后AC。
VP时,以为F题是树上博弈论之类的奇奇怪怪的东西,于是没敢写。VP结束后,发现就是个树上最大匹配罢了。若最大匹配能覆盖所有点,则Bob胜;否则Alice胜。
代码实现也很简单,在树上跑一遍dfs即可。若回溯的时候,x的子树中存在没有匹配的点,就用自己匹配它;否则的话,x成为一个没有匹配的点。具体看代码:
int dfs(int x,int fa)
{
int siz=0;//siz为x的子树中没有匹配的点的数量
for(int i=head[x];i;i=e[i].nex)
if(e[i].to!=fa)
siz+=dfs(e[i].to,x);
if(siz) siz--;//子树中存在没有匹配的点,用自己匹配它,没有匹配的点的数量减一
else siz=1;//子树中不存在没有匹配的点,于是自己成为一个没有匹配的点
return siz;
}
题意:有n个数呈环形排列,每次操作可以修改其中的一个数,将它变成它和它相邻的两个数字中的最大值或最小值,求最少操作次数,使得这n个数都变成同一个数k。对于1-m的每个整数k,输出最少操作次数。
显然,若这n个数中没有k的话,我们不可能将这n个数都变成k。而当这n个数中存在k的话,我们一定可以通过有限次数将这n个数都变成k。
首先,我们可以把所有小于k的数字看作-1,所有等于k的数字看作0,所有大于k的数字看作1。而我们的任务就是将所有的-1和1修改为0。
考虑这样一种情况:序列中没有1和-1的交替。这时操作次数就是1和-1的个数。
比较麻烦的情况是,序列中存在1和-1的交替,那么我们需要通过额外的操作次数,将这样的交替序列抹除。记交替序列的长度为len,那么显然我们需要至少[len/2]次操作,来将所有的1(-1)变成-1(1),之后就变成了上面的情况。
于是问题转换成了:求序列中1和-1的交替序列的个数及长度。
由于查询是单调的,所以当一个数被看作-1后,它将不会再被改变。考虑到这一点,我们只需实现单点修改操作,那么开一个线段树记录交错序列长度,每次单点修改更新即可。
其中,合并两个子区间的代码如下:
tree merge(tree ls,tree rs,int l,int r)
{
tree fa;
int mid=(l+r)>>1;
if(b[mid]&&b[mid+1]&&b[mid]+b[mid+1]==0)
{
if(ls.len1==mid-l+1) fa.len1=ls.len1+rs.len1;
else fa.len1=ls.len1;
if(rs.len2==r-mid) fa.len2=ls.len2+rs.len2;
else fa.len2=rs.len2;
fa.ans=ls.ans+rs.ans-(ls.len2/2+rs.len1/2)+(ls.len2+rs.len1)/2;
}
else
{
fa.len1=ls.len1;
fa.len2=rs.len2;
fa.ans=ls.ans+rs.ans;
}
return fa;
}
按题意枚举即可。从全部是摩托车的情况,枚举到全部是汽车的情况。
具体见代码:
while(r-l+1>=k)
{
if(a[r]+d<lc) break;
car++;
if(a[r]<lc) cost+=(lc-a[r]);
if(a[r]<lm) cost-=(lm-a[r]);
else rest-=min(d,a[r]-lm);
for(int i=l;i<l+k-1;i++)
{
if(a[i]+d<lm) motor--;
else if(a[i]<lm) cost-=(lm-a[i]);
rest+=min(d,a[i]-1);
}
if(rest>=cost&&motor==0) ans=min(ans,car*pc+(n-car*k)*pm+cost*t);
l+=k-1,r--;
}