Codeforces #342 Div2 硬核场
A Guest From the Past (毒瘤贪心推公式题)
题意
买塑料瓶的酒花A元,买玻璃瓶的酒花B元但是返还C元。问用N元最多他能喝多少瓶酒。
思路
贪心,怎么便宜怎么买。玻璃瓶的酒实际上是(B-C)元价格,但是有个B的门槛,不够B元就只能买塑料瓶。
如果A比(B-C)还便宜,那直接买塑料瓶就可以了,但是需要注意的是即使玻璃瓶换算起来比塑料瓶便宜但是如果买不起玻璃瓶装,那还是得买塑料瓶。
如果(B-C)比A便宜,那么先买玻璃瓶买到不足B元再买塑料瓶。这个过程不能模拟,因为数据范围太大(1e18)了,会T。
关于这个门槛,可以选择先减去一个C,表示最后肯定有C元剩下,因为最后一次买完玻璃瓶会返回一个C元这个C元和花完差价以后剩下的钱后面肯定再买不起玻璃瓶了。
TLE on test 48 //1e18不能用while模拟
WA on test 53 //如果金额不足C元直接说一瓶都买不起,别减成负数了
WA on test 59 //塑料瓶比较便宜但是门槛高
如果这场CF是真实CF的话,我已经死了,被Hack得内裤都不剩了。
代码
#include <iostream>
using namespace std;
int main() {
long long n,a,b,c;
cin>>n>>a>>b>>c;
if(b-c>=a || n<b){//虽然玻璃瓶理论更便宜但是买不起也没办法,还是得买塑料瓶
cout<<n/a<<endl;
}
else{
long long num;
long long counter=0;
if(n<c)
{
cout<<0<<endl;//一开始买不起玻璃瓶别把钱花成负数
return 0;
}
n-=c;
counter+=n/(b-c);//能买多少玻璃瓶
n%=(b-c);//计算余钱拿来还买塑料瓶
n+=c;
if(n>=b)
{
counter++;
n-=b;
n+=c;
}
counter+=n/a;//买塑料瓶
cout<<counter<<endl;
}
return 0;
}
B War of the Corporations (字符串暴力匹配)
题意
谷歌出了新平板,但苹果不服,说侵犯了他100年前的iPhone的专利。谷歌要求把自己平板名字里所有带有iPhone名称的子串用最少的#号替代掉。
思路
因为第二个字符串长度为30,第一个也才1e5,所以完全可以暴力匹配,只要匹配到,就把最后一个字符换成#(尽量对后面的子串作出贡献)。
但是需要注意的是字符串失配以后不应该从第一个失配的去找,还是应该找前面匹配过的有没有和第二个字符串首字母相同的项。
KMP肯定能做,但是暴力能做的东西为啥不暴力呢。
WA on test 54 //没有考虑到B字符串后面可能有失配部分回来重新匹配到的情况
WA on test 19 //就是回去找已匹配的时候傻了,移动了第二个字符串的坐标而不是第一个字符串的坐标
估计也是被hack,死得明明白白
代码
#include <iostream>
using namespace std;
int main() {
string a;
string b;
cin>>a>>b;
int j=0;
int counter=0;
for(int i=0;i<a.size();i++)
{
if(a[i]==b[j])
{
j++;
if(j==b.size())
{
counter++;
j=0;
}
}
else
{
for(int k=i-j+1;k<=i;k++)
{
if(a[k]==b[0])//从上一个找到了的字符串b的开头开始重新匹配
{
i=k;
j=0;
}
}
j=0;
if(a[i]==b[j])
{
j++;
if(j==b.size())
{
counter++;
j=0;
}
}
}
}
cout<<counter<<endl;
return 0;
}
C K-special Tables(找规律贪心构造)
题意
本场最简单。要求摆放1~n2{n^2}n2数字进n×n{n \times n}n×n 矩阵,使得每行元素递增,第k列元素尽量大。
思路
只要把最小的n×(k−1){n\times (k-1)}n×(k−1)个数字安排在左边k-1列,剩下的数字安排在右边即可。简单一发过,wa test 1是题意没看清,没输出第k列的和。
代码
#include <iostream>
using namespace std;
long long maps[505][505];
int main() {
int n,k;
cin>>n>>k;
long long num=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<k-1;j++)
{
maps[i][j]=num;
num++;
}
}
for(int i=0;i<n;i++)
{
for(int j=k-1;j<n;j++)
{
maps[i][j]=num;
num++;
}
}
long long counter=0;
for(int i=0;i<n;i++)
{
counter+=maps[i][k-1];//求第k列和
}
cout<<counter<<endl;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cout<<maps[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
D Final in arithmetic(找规律毒瘤大模拟构造)
题意
找形如abcd + dcba = efgh的数字,输出任意的abcd即可,然而数据达到了10100000{10^{100000}}10100000注定是个大字符串模拟。。。
wa了我15发,别说了要落泪了
思路
从字符串的两边开始找。
-
如果两边相等(比如记为数字now1=now2{now_1=now_2}now1=now2) 那么就把待定字符串n1{n_1}n1右部添上一个now1−⌊now1÷2⌋{now_1-\lfloor now_1\div2\rfloor}now1−⌊now1÷2⌋ 待定字符串n2{n_2}n2左部添上⌊now1÷2⌋{\lfloor now_1\div2\rfloor}⌊now1÷2⌋.这样能保证第一个数字不出现前导零。
-
如果now1=now2+1{now_1=now_2+1}now1=now2+1那么就让now1now_1now1给它右方的数字借一位,右方的数字加上10(我用了一个进位标志稍后处理)再去1判断。
-
如果此时now1≥10{now_1\geq10}now1≥10则要让now2now_2now2也大于等于10,从now2{now_2}now2左方借位,左边的数字减去1.我同样用了借位标志,再去1判断。
-
如果此时2和3的操作都不能使now1=now2{now_1 = now_2}now1=now2,那么就判断不能找到。
-
如果now1{now_1}now1和now2{now_2}now2重合,还得判断奇偶,奇数就判断不行,偶数就判断可以,并只给n1n_1n1或n2{n_2}n2添上now1÷2{now_1 \div 2}now1÷2
-
如果now1=1now_1=1now1=1且和now2now_2now2至少隔了一个数字也不相等,那么这个1也别急着扔了说不行,它可以进位给下一个数字,右边now2now_2now2 则不要动,等着匹配。如果now2now_2now2刚巧是0的话,也可以匹配掉把now2now_2now2也向前移动,给n1n_1n1 和n2n_2n2各添上一个0。注意一下前导0的特判,如果前面不能加前导0,那么就别把这个0匹配了,把now2now_2now2留给进位过的下一个now1now_1now1匹配。
-
最后把n1n_1n1 和n2n_2n2 拼起来就行了。
代码
#include <iostream>
using namespace std;
int main() {
string n;
cin >> n;
int i = 0;
int j = n.length() - 1;//从两边往中间匹配
int cy = 0;
int cx = 0;
string n1, n2;
bool flag = true;
while (i < n.size() && j >= i) {
int now1 = n[i] - '0';
int now2 = n[j] - '0';
if (cy == 1 && i == j) {//进位标志为1那么要进位
now2 += 10;//如果i和j重合,那么两个数保持一致地进位
}
if (cy == 1) {
now1 += 10;
cy = 0;
}
if (cx == 1) {//借位标志为1那么要借位
now2--;
if (now2 < 0) {//不够借还要往前借位
now2 += 10;
if (j - 1 == i) {//前面一个刚好是now1的话一定记得要让now1直接更新了
now1--;
if(now1 < 0){//前面的东西已经被处理完了,不可能借位了,直接判断不行。这里没卡
flag = false;
break;
}
else{
cx = 0;//其实无所谓了,毕竟这里循环就结束了
}
}
} else {
cx = 0;//如果还借位的话就不用置0,下次一起算
}
}
if (now1 < now2 && now1 != 1) {
//前面的比后面小进位都没用,直接不匹配,但如果是1还能和后面一起算
flag = false;
break;
}
if (now1 == 1 && now2 != now1 && i < j - 1) {
cy = 1;
now1--;
if (now1 == now2 && n1.size() != 0) {//不是前导0的话这里当作正常的匹配情况处理
n1 += '0';
n2 = '0' + n2;
j--;
}
i++;//下一个now1加10以后和当前的now2匹配,所以j不变
continue;
}
if (now1 >= 10 && now2 < now1 - 1) {
//now1大于10了,可能需要借位,但是如果now1和now2只差1的话,可以直接往下了,不需要借位
cx = 1;
now2 += 10;
if (i == j - 1) {
cx = 0;
now1--;//如果借的刚好是now1,那直接更新
}
}
if (now1 == now2 + 1) {
now1--;
cy = 1;//进位给下一个就可以匹配了
if (i + 1 == j && now1 < 10 && now2 < 10) {
flag = false;
break;
}
} else if (now1 != now2) {
flag = false;
break;
}
if (i == j) {
if (now1 % 2 == 1) {//如果i j重合,即长度出现奇数情况,不是偶数的话无法分配
flag = false;
break;
}
n1 += ('0' + now1 / 2);//因为是奇数长度,只给一边添加
break;
}
if (now1 - now1 / 2 >= 10) {//溢出,10以内的数字无法凑到这么大(比如19)
flag = false;
break;
}
n1 += '0' + (now1 - now1 / 2);
n2 = char('0' + now1 / 2) + n2;//匹配上了,拆成两半是最不可能出现前导0的
i++;
j--;
}
if (!flag) {
cout << 0 << endl;
} else {
cout << n1 + n2 << endl;
}
return 0;
}
感谢队友阿忠哥的启发,其他没有参考任何文献,自己面向数据编程了。。。