[赛记] csp-s模拟10
欧几里得的噩梦 -pts
看见是个线性基的题就没打;
其实也不是线性基,只不过模拟了一下它的过程;
考虑插入一位数时它能不能被表示,其实就是它异或上线性基中的某些值后可不可以为 $ 0 $,那么我们就可以将插入的单独一位数与 $ 0 $ 连边,将两位数互相连边,这样每插入一位数时看看它与 $ 0 $ 连不连通即可,这个可以用并查集实现;
时间复杂度:$ \Theta(n \alpha(m)) $;
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
int n, m;
int fa[500005];
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
int rem[500005];
int ksm(int a, int b) {
int ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
namespace LB{
void add(int id, int x, int y) {
x = find(x);
y = find(y);
if (x == y) return;
if (x < y) swap(x, y);
if (x != 0 || y != 0) {
fa[x] = y;
rem[++rem[0]] = id;
}
}
}
using namespace LB;
signed main() {
freopen("Euclid.in", "r", stdin);
freopen("Euclid.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++) fa[i] = i;
int s;
int x, y;
for (int i = 1; i <= n; i++) {
cin >> s;
if (s == 1) {
cin >> x;
add(i, x, 0);
}
if (s == 2) {
cin >> x >> y;
if (x < y) swap(x, y);
add(i, x, y);
}
}
cout << ksm(2, rem[0]) << ' ' << rem[0] << '\n';
for (int i = 1; i <= rem[0]; i++) cout << rem[i] << ' ';
return 0;
}
购物 0pts
赛时没打;
引用题解:这种题先想单个k怎么判断,找充要条件。
考虑对于一个数 $ x $ ,它的合法的 $ k $ 值范围为 $ [\lceil \frac x2 \rceil, x] $,所以我们可以找出所有数,求所有的区间的并即可;
找出所有的数复杂度是指数级的,是不行的;
所以考虑单个 $ k $,发现如果原序列存在 $ [k, 2k] $ 的数,那么这个 $ k $ 合法,否则找所有小于 $ k $ 的数之和,如果这个和大于等于 $ k $ 也合法(因为这样就保证了有落在 $ [k, 2k] $ 中的和),所以我们只需维护一个前缀和,然后将所有 $ n $ 个数和 $ n - 1 $ 个前缀和所构成的 $ [\lceil \frac x2 \rceil, x] $ 的区间求并即可;
对于区间求并,可以珂朵莉树维护(其实就是一个 set
);
时间复杂度:$ \Theta(n \log n) $;
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
int n;
long long a[500005];
long long ans;
long long w(long long a, long long b) {
if (a % b == 0) return a / b;
else return a / b + 1;
}
struct sss{
mutable long long l, r;
bool operator <(const sss &A) const {
return l < A.l;
}
};
set<sss> s;
void add(long long l, long long r) {
if (s.empty()) {
s.insert({l, r});
return;
}
set<sss>::iterator it = s.lower_bound({l, 0});
if (it == s.end()) {
it--;
if (it -> r < l) {
s.insert({l, r});
return;
}
}
if (it -> l > l) it--;
it -> l = min(it -> l, l);
it -> r = max(it -> r, r);
it++;
for (set<sss>::iterator i = it; i != s.end(); i++) {
if (i -> l > r) break;
if (i -> r <= r) s.erase(i);
else {
i -> l = r + 1;
break;
}
}
}
int main() {
freopen("buy.in", "r", stdin);
freopen("buy.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++) {
long long l = w(a[i], 2);
add(l, a[i]);
}
long long sum = a[1];
for (int i = 2; i <= n; i++) {
sum += a[i];
long long l = w(sum, 2);
add(l, sum);
}
for (set<sss>::iterator it = s.begin(); it != s.end(); it++) {
ans += (it -> r - it -> l + 1);
}
cout << ans;
return 0;
}
ants 50pts
就这道题拿分了。。。;
赛时用的线段树维护莫队,时间复杂度 $ \Theta(n \sqrt n \log n) \ TLE $ 了;
正解:回滚莫队;
其实这题主要的时间开销在删除上,所以考虑用只加不减的回滚莫队;
每次用并查集维护一下最长值域联通段,然后撤销一下即可;
赛时把回滚莫队复杂度记成 $ \Theta(n^{\frac53}) $ 导致没打,其实这是带修莫队的时间复杂度;
时间复杂度: $ \Theta(n \sqrt n \log n) $,但是并查集常数小,所以能过;
好像还有 $ \Theta(1) $ 链表维护的做法,没写;
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
int n, m;
int a[100005];
int st[505], ed[505], belog[100005];
int sq;
struct sss{
int l, r, id;
inline bool operator <(const sss &A) const {
if (belog[l] != belog[A.l]) {
return l < A.l;
} else {
return r < A.r;
}
}
}e[100005];
int fa[100005], siz[100005], ans[100005];
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
stack<pair<pair<bool, int>, pair<int, int> > > s;
vector<int> v;
bool vis[100005];
int ma, lans;
void add(int o, int x) {
vis[x] = true;
if (o == 1) s.push({{1, x}, {fa[find(x)], siz[find(x)]}});
if (!vis[x - 1] && !vis[x + 1]) return;
if (vis[x - 1]) {
if (o == 1) s.push({{0, find(x - 1)}, {fa[find(x - 1)], siz[find(x - 1)]}});
siz[find(x - 1)] += siz[find(x)];
siz[find(x)] = 1;
fa[find(x)] = find(x - 1);
ma = max(ma, siz[find(x - 1)]);
}
if (vis[x + 1]) {
if (o == 1) s.push({{0, find(x + 1)}, {fa[find(x + 1)], siz[find(x + 1)]}});
siz[find(x)] += siz[find(x + 1)];
siz[find(x + 1)] = 1;
fa[find(x + 1)] = find(x);
ma = max(ma, siz[find(x)]);
}
}
int main() {
freopen("ants.in", "r", stdin);
freopen("ants.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> e[i].l >> e[i].r;
e[i].id = i;
}
sq = sqrt(n);
for (int i = 1; i <= sq; i++) {
st[i] = (i - 1) * sq + 1;
ed[i] = i * sq;
}
ed[sq] = n;
for (int i = 1; i <= sq; i++) {
for (int j = st[i]; j <= ed[i]; j++) {
belog[j] = i;
}
}
for (int i = 1; i <= n; i++) {
fa[i] = i;
siz[i] = 1;
}
sort(e + 1, e + 1 + m);
int l = 1;
int r = 0;
int ls = 0;
for (int i = 1; i <= m; i++) {
ma = 1;
if (belog[e[i].l] == belog[e[i].r]) {
v.clear();
for (int j = e[i].l; j <= e[i].r; j++) {
v.push_back(a[j]);
}
v.push_back(0x3f3f3f3f);
sort(v.begin(), v.end());
int an = 0, sum = 1;
for (int j = 0; j < v.size(); j++) {
if (v[j + 1] != v[j] + 1) {
an = max(an, sum);
sum = 1;
} else {
sum++;
}
}
ans[e[i].id] = an;
continue;
}
if (belog[e[i].l] != ls) {
lans = 1;
ls = belog[e[i].l];
while(!s.empty()) s.pop();
for (int j = 1; j <= n; j++) {
fa[j] = j;
siz[j] = 1;
vis[j] = false;
}
l = ed[belog[e[i].l]];
r = ed[belog[e[i].l]] - 1;
}
if (l < ed[belog[e[i].l]]) {
while(!s.empty()) {
pair<pair<bool, int> , pair<int, int> > p = s.top();
s.pop();
if (p.first.first) vis[p.first.second] = false;
fa[p.first.second] = p.second.first;
siz[p.first.second] = p.second.second;
}
l = ed[belog[e[i].l]];
}
while(r < e[i].r) add(0, a[++r]);
lans = max(ma, lans);
while(l > e[i].l) add(1, a[--l]);
ans[e[i].id] = max(lans, ma);
}
for (int i = 1; i <= m; i++) {
cout << ans[i] << '\n';
}
return 0;
}