整体评价:比较简单的一套题,前三道题大致相当于Codeforces div 2的T1、T2难度,第四题在div 1的T1、T2难度之间。
难度排序:A < C < B < D
简单题,开个数组记录一下每位选手的分组和理论分数;然后分别计算每个队伍的项目得分。最后计算所有学生的分数并排序即可(可以用结构体捆绑分组和学生成绩)。注意需要四舍五入的细节。
#include
#include
#include
using namespace std;
struct newdata
{
int score,type;
};
int n,k;
newdata student[1001];
int score2[27][27];
int score3[27];
bool cmp(newdata i,newdata j)
{
return (i.score < j.score || (i.score == j.score && i.type > j.type));
}
int main()
{
scanf("%d%d",&n,&k);
for (int i = 1;i <= n;i ++)
{
char c;
scanf("%d %c",&student[i].score,&c);
student[i].type = c - 'A' + 1;
}
for (int i = 1;i <= k;i ++)
for (int j = 1;j <= k;j ++)
scanf("%d",&score2[i][j]);
for (int i = 1;i <= k;i ++)
{
int sum = 0;
for (int j = 1;j <= k;j ++)
sum += score2[j][i];
double average = double(sum) / k;
int delta = 0;
for (int j = 1;j <= k;j ++)
if (abs(average - score2[j][i]) > 15)
{
delta ++;
sum -= score2[j][i];
}
score3[i] = round(double(sum) / (k - delta));
}
for (int i = 1;i <= n;i ++)
student[i].score = round(student[i].score * 0.6 + score3[student[i].type] * 0.4);
sort(student + 1,student + n + 1,cmp);
for (int i = n;i >= 1;i --)
printf("%d %c\n",student[i].score,char(student[i].type + 'A' - 1));
return 0;
}
稍微有点思维难度,但不难发现,这本质就是一个动态规划的套路题。
讨论具体成绩数值是没有意义的,我们只需要从原数列看出它们对应的大小关系,于是很容易联想到类似于【合唱队形】这道经典题,即对于每个学生,我们取它连续上升序列和连续下降序列中的最大值即可。
据说有人想到差分约束系统,那就有点小题大做了。
#include
#include
using namespace std;
int n;
int a[1000002];
long long dp[1000002];
long long dp2[1000002];
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i ++)
{
scanf("%d",&a[i]);
if (a[i] > a[i - 1])
dp[i] = dp[i - 1] + 1;
else dp[i] = 1;
}
dp2[n] = 1;
for (int i = n - 1;i >= 1;i --)
if (a[i] > a[i + 1])
dp2[i] = dp2[i + 1] + 1;
else dp2[i] = 1;
long long ans = 0;
for (int i = 1;i <= n;i ++)
ans += max(dp[i],dp2[i]);
printf("%lld",ans);
return 0;
}
乍一看还以为要用位运算性质,似乎很难。但实际上就是道简单题,直接把所有可能的数存进数组中,排下序统计连续相同数的个数的最大值即可。
细节是要判断a和a xor b是否相同,因为这个WA一次。
#include
#include
using namespace std;
int n,cnt;
long long num[2000001];
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i ++)
{
long long a;
scanf("%lld",&a);
num[++cnt] = a;
long long b;
scanf("%lld",&b);
if (a != (a ^ b))
num[++cnt] = a ^ b;
}
sort(num + 1,num + cnt + 1);
int now = 1;
long long ans = num[1];
int tot = 1;
for (int i = 2;i <= cnt;i ++)
if (num[i] != num[i - 1])
{
if (now > tot)
{
tot = now;
ans = num[i - 1];
}
now = 1;
}
else now ++;
if (now > tot)
ans = num[cnt];
printf("%lld",ans);
return 0;
}
其实做完前三题我还有两个半小时多一点,但是第四题想复杂了最后放弃。
十六进制转二进制没什么好说,每一位对应四位后去掉前缀0。
后面的想法:做过很多翻硬币的题,加之前几天也看了一些关于奇偶性问题的资料,结果以为是要用数学方法。于是先考虑如何判断无解。想法是每个1可以左移或右移3位,于是考虑所有1是否都在3的倍数上。但是这种思路显然错误,因为在考虑多个1的反转时,不能简单地以单个1叠加。
后面看了题解才发现原来不是数学问题,就是单纯地贪心。容易发现选取每个中心点翻转两次后与没翻转是等价的,于是每个中心点最多翻转一次。那么只需要从左往右枚举所有中心点,如果中心点左边为1则翻转,否则不翻转即可。
特殊情况:由于首尾必定为1,要讨论第一次翻第一位还是第二位并取最小值,若都无解,则输出"No".
#include
#include
#include
#include
using namespace std;
string s;
bool t[4000001];
bool a[4000001];
int cnt,st;
int main()
{
cin >> s;
for (int i = 0;i < s.size();i ++)
if (s[i] >= '0' && s[i] <= '9')
{
int k = s[i] - '0';
for (int j = 3;j >= 0;j --)
t[++cnt] = ((k >> j) & 1);
}
else
{
int k = s[i] - 'A' + 10;
for (int j = 3;j >= 0;j --)
t[++cnt] = ((k >> j) & 1);
}
for (int i = 1;i <= cnt;i ++)
if (t[i])
{
st = i;
break;
}
if (cnt - st + 1 <= 2)
{
if (st == cnt)
printf("%d",t[st]);
else if (t[st] ^ t[cnt])
printf("No");
else printf("%d",t[st]);
return 0;
}
int sum = 1;
for (int i = st;i <= cnt;i ++)
a[i] = t[i];
a[st] = 0;
a[st + 1] ^= 1;
for (int i = st + 1;i < cnt - 1;i ++)
if (a[i])
{
sum ++;
a[i] ^= 1;
a[i + 1] ^= 1;
a[i + 2] ^= 1;
}
if (a[cnt - 1] && a[cnt])
sum ++;
else if (a[cnt - 1] ^ a[cnt])
sum = -1;
int ans = sum;
sum = 1;
for (int i = st;i <= cnt;i ++)
a[i] = t[i];
a[st] = 0;
a[st + 1] ^= 1;
a[st + 2] ^= 1;
for (int i = st + 1;i < cnt - 1;i ++)
if (a[i])
{
sum ++;
a[i] ^= 1;
a[i + 1] ^= 1;
a[i + 2] ^= 1;
}
if (a[cnt - 1] && a[cnt])
sum ++;
else if (a[cnt - 1] ^ a[cnt])
sum = -1;
if (ans == -1 && sum == -1)
printf("No");
else if (sum == -1)
printf("%d",ans);
else if (ans == -1)
printf("%d",sum);
else printf("%d",min(ans,sum));
return 0;
}
最后排名25,三题榜首但没AK,有点可惜啊。