A - Divisible
大意
给定个数,对于其中能被整除的数,输出商。
思路
直接计算即可。
代码
#include<iostream>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
while(n--){
int x;
cin >> x;
if(x % k == 0) cout << (x / k) << " ";
}
return 0;
}
B - Substring
大意
给定字符串,输出它不同的子串个数(不包括空串)。
思路
把所有子串丢进集合里,最后输出集合的大小。
代码
#include<iostream>
#include<set>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
string s;
set<string> subs;
cin >> s;
n = s.size();
for(int i = 0; i < n; i++)
for(int j = i; j < n; j++)
subs.insert(s.substr(i, j - i + 1));
cout << subs.size() << endl;
return 0;
}
C - Ideal Holidays
大意
一种历法,一周有天,前天是节假日,其余为工作日。
给定一些计划,输出它们是否都安排在节假日。
思路
最重要的是星期几,因此所有模上。
问题被转化为:数轴上有一些点,问能否有一个长度为的区间覆盖所有点。
先排序,然后枚举左端点,看与最右边的点的距离是否超过 即可。
注意会爆long long。
代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, a, b;
cin >> n >> a >> b;
int tot = a + b;
vector<int> dis;
for(int i = 0; i < n; i++){
int x; cin >> x; x--;
x %= tot;
dis.push_back(x);
dis.push_back(tot + x);
}
sort(dis.begin(), dis.end());
int ans = 1e18;
for(int i = 0; i < n; i++)
ans = min(ans, dis[i + n - 1] - dis[i] + 1);
cout << (ans <= a? "Yes": "No") << endl;
return 0;
}
D - Popcount and XOR
大意
给定,解下列方程组,要求,若无解输出-1:
给定任意一组解均可。
思路
令,那么这个1需要分配到中。
设分配了个1给,个1给,并且。
那么剩下的1需要在xor时抵消掉。
易得,结合可解得
具体解法为:
其中就是需要抵消掉的的数量。
我们逐位遍历,如果是,则分配给或(看的值),如果是,就同时分配给两个数。
无解条件就是上面的方程没有自然数解,即下面几种情况:
- 或
- 还有一种难以言说的情况,需要求出答案后验证一下。
代码
#include<iostream>
#include<cassert>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
auto popcount = [&](int x) -> int{
int ret = 0;
while (x) {
ret += x & 1;
x >>= 1;
}
return ret;
};
int a, b, c;
cin >> a >> b >> c;
int aa = a, bb = b, cc = popcount(c);
if(cc > a + b || ((a + b - cc) & 1)){
cout << -1 << endl;
return 0;
}
int over = (a + b - cc) / 2;
a -= over;
b -= over;
if(a < 0 || b < 0){
cout << -1 << endl;
return 0;
}
int x = 0, y = 0;
for(int i = 0; i < 60; i++){
if(c & (1ll << i)){
if(a){
x |= (1ll << i);
a--;
} else if(b){
y |= (1ll << i);
b--;
}
}else if(over){
x |= (1ll << i);
y |= (1ll << i);
over--;
}else assert(0);
}
if(popcount(x) != aa || popcount(y) != bb) cout << -1 << endl;
else cout << x << " " << y << endl;
return 0;
}
E - Set Add Query
大意
初始时,有一个包含个的数组和一个空集。
有次操作,每次操作给定一个,执行以下动作:
- 如果在集合中,那就移除它,否则丢入集合。
- 对于每个满足的,如果,那么加上集合的大小。
执行所有操作后,输出。
思路
朴素的模拟的复杂度是,显然不可取。
考虑在这个过程,当一个数在集合中时,它在被移出集合之前,都会对做贡献。
注意到一个数 做出的贡献是一个连续的操作区间,贡献值就是这个操作区间的的和。
我们可以维护一个关于操作顺序的的前缀和,当集合中的一个数被移除时,我们就计算它的贡献(前缀和相减)。需要记录何时放入。
操作完后,对还在集合里的数结算贡献即可。
代码
#include<iostream>
#include<set>
#include<vector>
using namespace std;
#define int long long
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, q;
cin >> n >> q;
vector<int> pre(1, 0), la(n, 0), ans(n, 0);
set<int> s;
for(int i = 1; i <= q; i++){
int x; cin >> x; x--;
if(s.count(x)){
ans[x] += pre[i - 1] - pre[la[x] - 1];
s.erase(x);
}else{
la[x] = i;
s.insert(x);
}
pre.push_back(pre.back() + s.size());
}
for(auto &i: s) ans[i] += pre[q] - pre[la[i] - 1];
for(auto &i: ans) cout << i << " ";
return 0;
}