A - Sum of Consecutive Prime Numbers
题意:给定一个正整数,查询有多少个满足其和可以用连续的质素表示。
思路:因为这道题本身num不会很大(10000),其中包含的质数更少了,完全可以找出所有的素数,然后使用尺取。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
vector< ll > v;
ll sum[10010];
int main()
{
ios :: sync_with_stdio( false );
cin.tie( NULL );
v.push_back(2);
for( ll i = 3; i <= 10000; i++ ){
int f = 1;
for( ll j = 2; j <= sqrt( i * 1.0 ); j++ ){
/*
该错误显示sqrt函数后面参数只能是long double 、float或者double类型的数据,
如果给它一个整形的参数,系统报错,不知道该用哪个。有两个方法,一个是使用强转,
一个是使用乘上1.0,让系统自动转化为小数。
*/
if( i % j == 0 ){
f = 0;
break;
}
}
if( f ){
v.push_back(i);
}
}//这道题首先需要素数打表,其实有更高级的做法--素数筛,这里先不说。
ll len = v.size();
sum[0] = 0;
for( ll i = 1; i < len; i++ ){
sum[i] = sum[i - 1] + v[i - 1];
}//最开始想用前缀和,但是其实多此一举,后的题会说到这里。
ll num;
while( cin >> num && num != 0 ){
ll l = 0, r = 0;
ll res = v[0];
ll cnt = 0;
while( l < len && r < len ){
while( res < num && r < len ){
++r;
res = sum[r] -sum[l] + v[r];
}
if( res == num ){
++cnt;
++l;
res = sum[r] -sum[l] + v[r];
}
while( res > num && l < len){
++l;
res = sum[r] -sum[l] + v[r];
}
}
cout << cnt << endl;
}
return 0;
}
C - Graveyard Design
题意:输入一个数(1 <= n <= 10^14),查看有多少种序列满足这种序列中的数是连续的自然数且 序列中各个位置上的数平方的和等于输入的数。
思路:最开始做这道题的时候,先后经历了超时,数组溢出,哭了...,后面发现理解的尺取有问题,难受,可怕就在上面那题居然以这样的方式过了,难受.....
这道题也相当于是尺取的板子。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
int main()
{
ios :: sync_with_stdio( false );
cin.tie( NULL );
ll num;
cin >> num;
ll l = 1, r = 1;//刚开始双指针都指向头
ll res = 0;
ll len = sqrt( num * 1.0 );//减小范围
vector< pair< ll, ll > > v;//用一个pair来记录这个序列的左右边界
while( true ){
while( res < num && r <= len){
//如果当前尺度还是小于num,那么右指针一直往右移动,但是要保证不要越界。
res += r * r;
++r;
}
//如果从上面的while退出了,说明要么res >= num,或者 r > len;
if( res < num ){
break;
}
//如果经过了上面的while,还是小于num,
//那就说明这个序列永远都不可能再满足条件了,因为r已经到最尾巴了.
if( res == num && l <= len){
v.push_back( make_pair( l, r - 1 ) );
}
//如果满足上述条件,那么就加入队列中
res -= l * l;
++l;
//这里为了方便代码书写,我们规定不论是大于还是等于,都把l向右移动。
}
len = v.size();
cout << len << endl;
for( ll i = 0; i < len; i++ ){
cout << v[i].second - v[i].first + 1;
for( ll j = v[i].first; j <= v[i].second; j++ ){
cout << " " << j;
}
cout << endl;
}
return 0;
}
D - Hyperset
题意:输入长度为k的n个字符串,且这个字符串只包含'S','E','T'三种字符,同时n个字符串都是不相同的,找出有多少对(三个为一队)满足对应位置上的字符要么相等,要么互补(SET)。
思路:任意枚举两个字符串,通过这两个字符串,找出对应的字符串,然后在整个字符串中寻找有没有这样的字符串。注意这种思想以后会常常遇到:一般就是常数级的,但是硬跑就会超时,这样就考虑合并。这道题有比较巧妙的地方:1、通过ascall码来记录 2、int 和 char,string的转换。 int sum = 'A' + 'B' + 'C'; string s; s += sum - 'A' - 'B'; PS:结果要除以 3。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
int sum = 'S' +'E' +'T';
int main()
{
ios :: sync_with_stdio( false );
cin.tie( NULL );
int n, k;
cin >> n >> k;
string s[1510];
set< string > st;
for( int i = 0; i < n; i++ ){
cin >> s[i];
st.insert( s[i] );
}
long long ans = 0;
for( int i = 0; i < n; i++ ){
for( int j = i + 1; j < n; j++ ){
string res;
for( int f = 0; f < k; f++ ){
if( s[i][f] == s[j][f] ){
res += s[i][f];
}
else{
res += sum - s[i][f] - s[j][f];
}
}
ans += st.count( res );
}
}
cout << ans / 3;
return 0;
}
B - Dyson Box
题意:给定n个方块的位置,有n种情况,每种情况都是在上面情况上累加的。分别计算在重力向下和向左的情况下,组成的图形的周长。
思路:这是一道思维+stl容器题。很明显每个正方体的周长贡献正常是4,但是在某些情况下,贡献就会减少。拿重力向下的来说,当它所在列本来就有其他方块的时候,它的贡献值就会-2,当它的前一列(后一列)的方块总数大于自己这一列的时候方块总数就会-2。重力方向向左也是一样的。如何记录?使用2个map来分别记录每一行和每一列的方块数量。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
map< ll, ll > mp1, mp2;
int main()
{
ios :: sync_with_stdio( false );
cin.tie( NULL );
ll n, x, y;
cin >> n;
ll vans = 0, hans = 0;
while( n-- ){
cin >> x >> y;
vans += 4; hans += 4;
if( mp1[x] )
vans -= 2;
if( mp2[y] )
hans -= 2;
if( x != 0 && mp1[x - 1] > mp1[x] )//注意细节,不要数组越界了
vans -= 2;
if( y != 0 && mp2[y - 1] > mp2[y] )
hans -= 2;
if( mp1[x + 1] > mp1[x] )
vans -= 2;
if( mp2[y + 1] > mp2[y] )
hans -= 2;
++mp1[x]; ++mp2[y];//注意这个不能放在前面,要先检查了再放
cout << vans << " " << hans << endl;
}
return 0;
}
总结:
学习了更多的常用stl容器,学习了基本的尺取知识和经典习题,对离散化有了一定的了解。离散化(Discretization),把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。通俗的说,离散化是在不改变数据相对大小的条件下,对数据进行相应的缩小。
vector:
v[ i ]:
v.empty( );
v.size();
v.clear( );
初始化:vector初始化总结
set :
s.clear( );
s.insert( );
s.erase( );
s.erase( first,last ) //删除迭代器范围内的所有元素
s.count( ) //查询这个数是否存在,存在返回1,反之返回0
s.empty( );
s.size();
find(x): 在 set 内存在键为 x 的元素时会返回该元素的迭代器,否则返回 end()。
map:
m.count( );
m.find( );
s.empty( );
s.size();
vector, 变长数组,倍增的思想
size() 返回元素个数
empty() 返回是否为空
clear() 清空
front()/back()
push_back()/pop_back()
begin()/end()
[]
支持比较运算,按字典序pair<int, int>
first, 第一个元素
second, 第二个元素
支持比较运算,以first为第一关键字,以second为第二关键字(字典序)string,字符串
size()/length() 返回字符串长度
empty()
clear()
substr(起始下标,(子串长度)) 返回子串
c_str() 返回字符串所在字符数组的起始地址queue, 队列
size()
empty()
push() 向队尾插入一个元素
front() 返回队头元素
back() 返回队尾元素
pop() 弹出队头元素priority_queue, 优先队列,默认是大根堆
size()
empty()
push() 插入一个元素
top() 返回堆顶元素
pop() 弹出堆顶元素
定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q;stack, 栈
size()
empty()
push() 向栈顶插入一个元素
top() 返回栈顶元素
pop() 弹出栈顶元素deque, 双端队列
size()
empty()
clear()
front()/back()
push_back()/pop_back()
push_front()/pop_front()
begin()/end()
[]set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列
size()
empty()
clear()
begin()/end()
++, -- 返回前驱和后继,时间复杂度 O(logn)set/multiset
insert() 插入一个数
find() 查找一个数
count() 返回某一个数的个数
erase()
(1) 输入是一个数x,删除所有x O(k + logn)
(2) 输入一个迭代器,删除这个迭代器
lower_bound()/upper_bound()
lower_bound(x) 返回大于等于x的最小的数的迭代器
upper_bound(x) 返回大于x的最小的数的迭代器
map/multimap
insert() 插入的数是一个pair
erase() 输入的参数是pair或者迭代器
find()
[] 注意multimap不支持此操作。 时间复杂度是 O(logn)
lower_bound()/upper_bound()unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表
和上面类似,增删改查的时间复杂度是 O(1)
不支持 lower_bound()/upper_bound(), 迭代器的++,--bitset, 圧位
bitset<10000> s;
~, &, |, ^
>>, <<
==, !=
[]count() 返回有多少个1
any() 判断是否至少有一个1
none() 判断是否全为0set() 把所有位置成1
set(k, v) 将第k位变成v
reset() 把所有位变成0
flip() 等价于~
flip(k) 把第k位取反