Winter Holiday Training Competition 2019, Post-competition Reflections
1、
Cutting Out
CodeForces - 1077D
这是第四场的训练赛中的一个题目,这个题就是利用二分找最优解,最后输出,挺常规的题目,当时在第40个样例T了4次,当时真的不甘心,现在看来只能怪自己的思维不够严谨。
一方面:
在二分的时候把 l 设置为0,那么假设只有右边的1成立,那么最后就会剩余0与1,在逼近最优解的时候,0 + 1 )/ 2 == 0 这时候逼近的是0到没关系,因为xx变量已经把最优解1保留着,0将会不成立,但是再判断时 0 这个解却会在分母上,导致程序崩溃。
如果确定最优解不是0,就尽量l设置为1,二分写法我现在也改变了原来的写作习惯,改成了如下所示:
int l = 1, r = n, xx = 1;
while(l <= r)
{
int mid = (l + r) >> 1;
if(f(mid)) l = mid + 1, xx = mid; //x保留最优解
else r = mid - 1;
}
另一方面:在最后输出的时候,没考虑好循环截止条件:
for(int i = k, j = 1; i > 0; j++)
{
int temp = a[j].time / xx;
for(int p = 1; p <= temp && i > 0; p++, i--)
printf("%d%c", a[j].val, i == 0 && p == temp ? '\n' : ' ');
}
这里当时 i 只是控制 i != 0 的时候,i < 0 的时候更不行,而且会出现 i < 0 的情况。
比如: 5 2 3 3 3 2 1 这一组样例就会让我Wa,因为这里确定的最优解是1,但是temp算出来是3 最后应输出2个值,但是由于我没有管i小于0的时候,于是呢,就凉凉了...
2、关于精度问题
Pythagorean Theorem II
这个题其实就是遍历一下,判断三条边是否可以构成直角三角形,求出来 a^2 + b^2 怎么判断c是不是整数呢?
这里比较简单的就是:
double c = sqrt(a * a + b * b);
int t = c;
if((int)c * (int)c == t) puts("c是整数")
这样处理判断比较方便,一开始判断的麻烦了,导致因为精度问题WA了一发,下不为例了。
3、string转化为小写字母
#include <iostream>
#include <algorithm>
using namespace std;
string s;
int main() {
cout<<"请输入一个含大写的字符串:";
string str;
cin>>str;
///转小写
transform(str.begin(),str.end(),str.begin(),::tolower);
cout<<"转化为小写后为:"<<str<<endl;
transform(str.begin(),str.end(),str.begin(),::toupper);
cout<<"转化为大写后为:"<<str<<endl;
return 0;
}
4、reverse
vector:
vector<int> v={1,2,3,4,5};
reverse(v.begin(),v.end());//v的值为5,4,3,2,1
整型数组也是可以的,用法同上;
string:
string str="C++REVERSE";
reverse(str.begin(),str.end());//str结果为ESREVER++C
5、X^Y
给你两个数 l , r (0 <= l, r <= 1e15)让你求解 l ^ (l + 1) ^ (l + 2) ^ ·····^ (r - 2) ^ (r - 1) ^ r 的结果。
只需注意到一点即可: (2 * n) ^ (2 * n + 1) = 1 一个偶数相邻一个奇数,由于奇数只在它前面偶数的二进制的第一位多一个1,所以亦或以后等于1,再利用 a^a = 0的特点,根据1的个数的奇偶性来最后结果。
6、取上|下整
取上整函数:ceil(double);
取下整函数:floor(double);
头文件:#include<cmath>或#include<math.h>
7、输入输出
scanf("%1d", &n);
printf("%02d\n", n);
/***结果***/
56
05
scanf("%1d", &n);
printf("%2d\n", n);
/***结果***/
56
5
8、范围问题 ***
The Super Powers
输出在范围 2^64 - 1范围内的,可以被拆成 至少两种数的n次方形式的数,例如:16 = 2^4 = 4 ^ 2 底数可以为2也可以为4.
所以一个数的指数只要是合数就可以,因为 (n^a)^b == (n^b)^a 能拆成像a * b 这样的数的应是合数。
所以先打表求1 - 64范围内的合数作为指数,又因为范围太大!!! unsigned long long 的上限值,所以直接算出来一个数然后与 2^64 - 1进行比较是否小于它不现实,直接爆 unsigned long long 了,所以可以利用数学的思维。
如果求出 i ^ k < 2 ^ 64 的k的取值范围不就好了嘛,超过k的直接不求了,因为求了也就超了 2^64了
i^k < 2 ^64
=> log(i^k) < log(2^64)
=> k * log(i) < 64 * log(2)
=> k < (64 * log(2)) / log(i)
所以终止求取i^k,的条件由 i^k < 2^64 - 1 转换为==》 k < (64 * log(2)) / log(i) 。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#define up(i, x, y) for(int i = x; i <= y; i++)
#define down(i, x, y) for(int i = x; i >= y; i--)
#define MAXN 64
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned long long ull;
int prime[64 + 1];
void getprime() //对合数打表
{
for(int i = 2; i <= 64; i++){
if(prime[i]) {prime[++prime[0]] = i; continue;}
for(int j = i * 2; j <= 64; j += i){
prime[j] = 1;
}
}
}
ull power(ull a, ull b)
{
ull base = a, ans = 1;
while(b)
{
if(b & 1){
ans *= base;
}
base *= base;
b >>= 1;
}
return ans;
}
int main()
{
getprime();
set<ull> set_;
ull n = 1 << 16;
set_.insert(1);
for(ull i = 2; i < n; i++)
{
double t = 64 * log(2) / log(i);
for(int j = 1; j <= prime[0] && prime[j] * 1.0 <= t; j++){
set_.insert(power(i, prime[j]));
}
}
set<ull> :: iterator iter = set_.begin();
while(iter != set_.end())
{
if(*iter > 0) cout<<*iter<<'\n';
iter++;
}
}
9、枚举
枚举答案时,一般固定一个,另一个用二分枚举,或者求出验证,二分!二分!二分!
10、数学运算符号
exp(n) = e^n
log(n) = 以e为底n的对数
log10(n) = 以10为底n的对数
log(n)/log(m) = 以m为底n的对数
PI = acos(-1)
eps = 10^-6
11、STL next_permutation() 与 prev_permutation()
头文件:#include<algorithm>
next_permutation()函数的返回类型是bool类型.
即:如果有一个更高的排列,它重新排列元素,并返回true;如果这是不可能的(因为它已经在最大可能的排列),它按升序排列重新元素,并返回false
#include<cstdio>
#include<algorithm>
using namespace std;
int arr[4];
int main()
{
arr[1] = 1, arr[2] = 2, arr[3] = 3;
do{
printf("%d %d %d\n", arr[1], arr[2], arr[3]);
}while(next_permutation(arr + 1, arr + 1 + 3 ));
}
#include<cstdio>
#include<algorithm>
using namespace std;
int arr[4];
int main()
{
arr[1] = 3, arr[2] = 2, arr[3] = 1;
do{
printf("%d %d %d\n", arr[1], arr[2], arr[3]);
}while(prev_permutation(arr + 1, arr + 1 + 3 ));
}
例题:https://www.luogu.org/problemnew/show/P1088
12、int16 int32 int64:
Int16 相当于 short 占2个字节 -32768 ~ 32767
Int32 相当于 int 占4个字节 -2147483648 ~ 2147483647
Int64 相当于 long 占8个字节 -9223372036854775808 ~ 9223372036854775807
13、进制转换:
1 - A 26 - Z 27 - AA 28 - AZ
void _10_26(int n)
{
stack<int> st;
while(n)
{
st.push((n - 1) % 26);
n = (n - 1) / 26;
}
while(!st.empty())
{
printf("%c", st.top() + 'A' );
st.pop();
}
}
14、格式输入方式:
char str[MAXN]; scanf("%s", &str);
int a, b;
if(sscanf(str, "R%dC%d", &a, &b) == 2)
{
}
else{}
15、字符串剪切
if(str[0] == '+') set_.insert(str.substr(1, str.length() - 1));
if(str[0] == '-') set_.erase(str.substr(1, str.length() - 1));
//str.substr(起点,长度)
16、string字符串反转
持续更新....