2020提高组正睿十连测
\(Day2\)
\(A\)
注意到排序后每次是选一个前缀,所以可以在线段树上二分,每次全局加上可以拿的物品,选了的清零即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const int INF = 0x7fffffff;
const int N = 200000;
int n, m, pos, c[N + 50];
ll sum[N + 50], ans, ssum[N + 50];
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
struct Node
{
int a, b;
} stone[N + 50];
struct Tag
{
ll x, y;
};
struct Sgementtree
{
Tag tag[(N << 2) + 50], tree[(N << 2) + 50];
long long sl[(N << 2) + 50], s[(N << 2) + 50];
void Add1(int k, int l, int r, int add)
{
s[k] += 1LL * add * (ssum[r] - ssum[l - 1]); sl[k] += 1LL * add * (sum[r] - sum[l - 1]);
tag[k].x += add;
return;
}
void Ql(int k)
{
s[k] = sl[k] = tag[k].x = 0; tag[k].y = 1;
return;
}
void Pushdown(int k, int l, int r, int mid)
{
if (tag[k].y)
Ql(k << 1), Ql(k << 1 | 1), tag[k].y = 0;
if (tag[k].x)
Add1(k << 1, l, mid, tag[k].x), Add1(k << 1 | 1, mid + 1, r, tag[k].x), tag[k].x = 0;
return;
}
void Pushup(int k)
{
s[k] = s[k << 1] + s[k << 1 | 1];
sl[k] = sl[k << 1] + sl[k << 1 | 1];
return;
}
ll Query(int k, int l, int r, int ask)
{
if (l == r)
{
tag[k].x = tag[k].y = 0;
s[k] -= 1LL * ask * stone[l].a;
sl[k] -= ask;
return 1LL * ask * stone[l].a;
}
int mid = (l + r) >> 1;
Pushdown(k, l, r, mid);
if (sl[k << 1] < ask)
{
ask -= sl[k << 1];
ll tmp = s[k << 1];
Ql(k << 1);
tmp += Query(k << 1 | 1, mid + 1, r, ask);
Pushup(k);
return tmp;
}
else
{
ll tmp = Query(k << 1, l, mid, ask);
Pushup(k);
return tmp;
}
}
} tr;
int Cmp(Node a, Node b)
{
return a.a < b.a;
}
int main()
{
Read(n); Read(m);
for (int i = 1; i <= n; i++) Read(c[i]);
for (int i = 1; i <= m; i++) Read(stone[i].a), Read(stone[i].b);
sort(stone + 1, stone + m + 1, Cmp);
for (int i = 1; i <= m; i++)
if (stone[i].b == -1)
{
pos = i;
break;
}
for (int i = 1; i <= pos - 1; i++) sum[i] = sum[i - 1] + stone[i].b, ssum[i] = ssum[i - 1] + 1LL * stone[i].a * stone[i].b;
sum[pos] = sum[pos - 1] + 1000001;
for (int i = 1; i <= n; i++)
{
tr.Add1(1, 1, pos, 1);
ans += tr.Query(1, 1, pos, c[i]);
}
cout << ans;
return 0;
}
\(B\)
经典结论只需重载\(sort\)中的\(cmp\)函数。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 300000;
string st[N + 50];
int n;
int Cmp(string a, string b)
{
return a + b < b + a;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) cin >> st[i];
sort(st + 1, st + n + 1, Cmp);
for (int i = 1; i <= n; i++) cout << st[i];
return 0;
}
\(C\)
推一下结论,发现题目转化为找一个权值最大的向量集合使得\(\mod 3\)下线性无关,用线性基维护,注意要用位运算优化三进制操作。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define ll long long
const int N = 500050;
ll lastans, ans;
int n;
struct Node
{
ll pos1, pos2; int zhi;
} a[60];
template<class T>
void Read(T &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
void Add(Node &a, const Node &b)
{
ll a0 = ~(a.pos1 | a.pos2), b0 = ~(b.pos1 | b.pos2);
a = (Node){(a.pos2 & b.pos2) | (a.pos1 & b0) | (a0 & b.pos1), (a.pos1 & b.pos1) | (a.pos2 & b0) | (a0 & b.pos2), a.zhi};
return;
}
void Del(Node &a, const Node &b)
{
ll a0 = ~(a.pos1 | a.pos2), b0 = ~(b.pos1 | b.pos2);
a = (Node){(a.pos2 & b.pos1) | (a.pos1 & b0) | (a0 & b.pos2), (a0 & b.pos1) | (a.pos2 & b0) | (a.pos1 & b.pos2), a.zhi};
return;
}
void Ins(Node tmp)
{
for (ll i = 59; i >= 0; i--)
if (((tmp.pos1 >> i) & 1) | ((tmp.pos2 >> i) & 1))
{
if (!a[i].zhi)
{
a[i] = tmp; ans += tmp.zhi;
return;
}
if (tmp.zhi > a[i].zhi)
{
ans += tmp.zhi - a[i].zhi;
swap(tmp, a[i]);
}
if (((tmp.pos1 >> i & 1) & (a[i].pos1 >> i & 1)) | ((tmp.pos2 >> i & 1) & (a[i].pos2 >> i & 1))) Del(tmp, a[i]);
else Add(tmp, a[i]);
}
return;
}
int main()
{
Read(n);
ll x; int y;
for (int i = 1; i <= n; i++)
{
Read(x); Read(y);
x = x ^ lastans;
Ins((Node){x, 0, y});
lastans = ans; printf("%lld\n", ans);
}
return 0;
}
\(Day3\)
\(A\)
每个点随机为工业城市或农业城市,考虑期望的线性性,一条边对答案的贡献是\(\frac{1}{2}\),那么总的期望好边数就是\(\frac{m}{2}\),那么必然有构造方式可以构造出一种方案使得变数\(> \frac{m}{2}\)。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, m;
int main()
{
scanf("%d%d", &n, &m);
printf(m == 0 ? "No\n" : "Yes\n");
}
\(B\)
发现操作可逆,于是可以将两个序列都转化成一个中间序列,如果可行则有解。
可以将中间序列定为能变成的字典序最小的序列,这里的字典序最小指在\(1\)的个数尽可能少的情况下\(1\)的位置尽量靠前。
题目中所给的操作等价于如果一个\(1\)前面有\(k\)个\(0\),那么它的位置可以往前移\(k\),如果有连续\(k\)个\(1\),可以都变为\(0\)。
这样子发现移动之后的位置\(\mod k\)不变,所以可以根据此性质进行模拟。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1000000;
int a[N + 50], b[N + 50], lena, lenb, n, k;
char sta[N + 50], stb[N + 50];
void Calc(char *st, int *a, int &len)
{
for (int i = 1; i <= n; i++)
{
if (st[i] == '0') continue;
if (!len) { a[++len] = i % k; continue; }
int u = i % k, lst = a[len];
if (u <= lst) u += ((lst - u) / k + 1) * k;
a[++len] = u;
while (len >= k && a[len] - k + 1 == a[len - k + 1]) len -= k;
}
while (len >= k && a[len] - k + 1 == a[len - k + 1]) len -= k;
return;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
lena = lenb = 0;
scanf("%d%d", &n, &k);
scanf("%s", sta + 1);
Calc(sta, a, lena);
scanf("%s", stb + 1);
Calc(stb, b, lenb);
if (lena != lenb) { puts("No"); continue; };
int flag = 0;
for (int i = 1; i <= lena; i++) if (a[i] != b[i]) { puts("No"); flag = 1; break; }
if (!flag) puts("Yes");
}
return 0;
}
\(C\)
发现可能的最大的序列的差值\(d\)是将所有必定在集合里的元素排序后两两之间的差的\(gcd\)。
设必须出现的最小元素为\(l\),最大为\(r\),然后在\([l,r]\)中的不能出现的元素的位置为\(pos\),那么\(pos - l\)的所有因子都不满足条件。
这样找出了所有可能作差的值,剩下就是看能向左向右延伸多远,发现还是拿\(pos\)去更新某个数的因子,这玩意可以高维前缀和,懒得写就直接暴力记录每个数的因子了。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define ll long long
const int INF = 0x7fffffff;
const int MOD = 1000000007;
const int M = 1000000;
ll n, b[M + 50], minn[M + 50], maxx[M + 50], L, R;
int m, p[M + 50], prime[M + 50], primecnt, cnt;
vector<ll> ones, zeros;
vector<int> g[M + 50];
template <class T>
void Read(T &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
ll Gcd(ll a, ll b)
{
return a % b == 0 ? b : Gcd(b, a % b);
}
void Prework()
{
p[0] = p[1] = 1;
for (int i = 2; i <= M; i++)
{
if (!p[i]) prime[++primecnt] = i;
for (int j = 1; j <= primecnt && prime[j] * i <= M; j++)
{
p[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
}
}
return;
}
ll Abs(ll x)
{
return x < 0 ? -x : x;
}
int Solve(ll d)
{
/* ll s = b[d];
for (int i = 1; i <= primecnt; i++)
{
ll tmp = 1;
while (s % prime[i] == 0)
{
tmp *= prime[i];
int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
minn[pos] = max(minn[pos], minn[d]);
maxx[pos] = min(maxx[pos], maxx[d]);
s /= prime[i];
}
}
if (s)
{
int pos = lower_bound(b + 1, b + cnt + 1, s) - b;
minn[pos] = max(minn[pos], minn[d]);
maxx[pos] = min(maxx[pos], maxx[d]);
}*/
// cout << b[d] << " " << minn[d] << " " << maxx[d] << endl;
if (minn[d] > maxx[d]) return 0;
return 1LL * ((maxx[d] - R) / b[d] + 1) * ((L - minn[d]) / b[d] + 1) % MOD;
}
int main()
{
Prework();
Read(n); Read(m);
int opt; ll x;
for (int i = 1; i <= m; i++)
{
Read(opt); Read(x);
if (opt) ones.push_back(x); else zeros.push_back(x);
}
sort(ones.begin(), ones.end());
ll maxd = ones[1] - ones[0];
for (int i = 2; i < ones.size(); i++) maxd = Gcd(maxd, ones[i] - ones[i - 1]);
L = ones[0], R = ones[ones.size() - 1];
ll d = maxd;
// cout << maxd << endl;
for (ll i = 1; i <= sqrt(maxd); i++)
if (maxd % i == 0)
{
b[++cnt] = i;
if (maxd / i != i) b[++cnt] = maxd / i;
}
sort(b + 1, b + cnt + 1);
for (int i = 1; i <= cnt; i++)
for (int j = 1; j <= i; j++)
if (b[i] % b[j] == 0)
g[i].push_back(j);
for (int i = 1; i <= cnt; i++) minn[i] = 1, maxx[i] = n/*, cout << i << " " << b[i] << endl*/;
for (vector<ll>::iterator it = zeros.begin(); it != zeros.end(); it++)
{
ll v = *it;
if (v >= L && v <= R)
{
ll tmp = Gcd(v - L, maxd);
int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
for (vector<int>::iterator it = g[pos].begin(); it != g[pos].end(); it++)
{
int u = *it;
minn[u] = INF; maxx[u] = -INF;
}
}
else if (v < L)
{
ll tmp = Gcd(L - v, maxd);
int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
for (vector<int>::iterator it = g[pos].begin(); it != g[pos].end(); it++)
{
int u = *it;
minn[u] = max(minn[u], v + 1);
}
}
else if (v > R)
{
ll tmp = Gcd(v - L, maxd);
int pos = lower_bound(b + 1, b + cnt + 1, tmp) - b;
for (vector<int>::iterator it = g[pos].begin(); it != g[pos].end(); it++)
{
int u = *it;
maxx[u] = min(maxx[u], v - 1);
}
}
}
int ans = 0;
for (int i = cnt; i >= 1; i--) ans = (ans + Solve(i)) % MOD/*, cout << Solve(i) << endl*/;
printf("%d", ans);
return 0;
}
\(Day5\)
\(A\)
这题结论\(10min\)就想出来了,但是题意读假了,以为必然连通。
直接粘题解吧,题解多详细(
首先考虑非空连通图的情况。题目中描述的是欧拉回路,根据相关知识,我们知道可行当且仅当每个点
度数均为偶数。那么问题转化为判断 阶线图是否每个点度数均为偶数。
假设\(k >= 1\) ,注意到\(k\)阶线图非空且每个点度数为偶数的话,意味着\(k - 1\)阶线图非空且每个点度数奇偶
性相同。再考虑\(k\)阶线图非空且每个点度数均为奇数的条件,当\(k >= 1\)时,这意味着\(k - 1\)阶线图每条边
两端节点度数奇偶性不同,也即,\(k - 1\)阶线图是一个非空的二分图,且每条边两端节点度数奇偶性不
同。可以发现这样的图如果不是\(K_{1,2}\)的话,一定存在度数\(> 2\)的节点但不存在三元环,因此不可能是任
意图的线图(否则考虑原图必然有度数$ > 2$的节点,因此它的线图会存在三元环,矛盾)。
这样我们就得到了一个非空连通图的判定算法:对于\(k = 0\)的情况,必定是每个点度数均为偶数;对于\(k >= 1\)
的情况,还允许每个点度数均为奇数;对于\(k >= 2\)的情况,还允许是一个二分图且每条边两端节点
度数奇偶性不同。
但其实这个算法仍然有很多漏洞:关键在于对于一个“链”状的连通块,它经过若干次求线图操作后会变
成单点甚至空图!因此我们还需要特殊考虑链状的连通块是否会变成单点或者空图,甚至即使有多个连
通块,也可能是有解的,需要进一步的讨论,这里就不展开了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
const int INF = 0x7fffffff;
const int N = 1000000;
using namespace std;
int n, m, k, fa[N + 50], ds[N + 50], p[N + 50], num[N + 50], xjds[N + 50], hh, tmpans, lian;
vector<int> pos[N + 50];
struct Bian
{
int u, v;
} edge[N + 50];
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
int Find(int x)
{
return fa[x] == x ? fa[x] : fa[x] = Find(fa[x]);
}
int Pd(int id)
{
if (num[id] != pos[id].size() - 1) return 0;
for (vector<int>::iterator it = pos[id].begin(); it != pos[id].end(); it++)
{
int v = *it;
if (ds[v] > 2) return 0;
}
return 1;
}
int Solve(int id)
{
int ods = 1;
for (vector<int>::iterator it = pos[id].begin(); it != pos[id].end(); it++)
{
int v = *it;
if (ds[v] & 1) { ods = 0; break; }
}
if (ods) return 0;
int jds = 1;
for (vector<int>::iterator it = pos[id].begin(); it != pos[id].end(); it++)
{
int v = *it;
if (!(ds[v] & 1)) { jds = 0; break; }
}
if (jds) return 1;
if (xjds[id]) return 2;
return INF;
}
int main()
{
Read(n); Read(m); Read(k);
if (m == 0) { puts("No"); return 0; }
if (!n) { puts("No"); return 0; }
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++)
{
Read(edge[i].u); Read(edge[i].v);
fa[Find(edge[i].u)] = Find(edge[i].v);
ds[edge[i].u]++; ds[edge[i].v]++;
}
for (int i = 1; i <= n; i++) fa[i] = Find(i), pos[fa[i]].push_back(i);
for (int i = 1; i <= m; i++) num[fa[edge[i].u]]++;
for (int i = 1; i <= n; i++)
if (!p[fa[i]])
{
if (Pd(fa[i]))
{
p[fa[i]] = 2;
if (pos[fa[i]].size() - 1 == tmpans) lian++;
if (pos[fa[i]].size() - 1 > tmpans) lian = 1, tmpans = pos[fa[i]].size() - 1;
}
else p[fa[i]] = 1;
}
for (int i = 1; i <= n; i++) xjds[i] = 1;
for (int i = 1; i <= m; i++)
if ((ds[edge[i].u] & 1) == (ds[edge[i].v] & 1))
xjds[fa[edge[i].u]] = 0;
int tmpans2 = 0;
for (int i = 1; i <= n; i++)
if (p[fa[i]] == 1)
{
hh++;
if (hh > 1) { puts("No"); return 0; }
p[fa[i]] = 0;
tmpans2 = Solve(fa[i]);
}
if (!hh && lian > 1) { puts("No"); return 0; }
if (hh && lian) tmpans++;
tmpans = max(tmpans, tmpans2);
if (!hh && tmpans != k) { puts("No"); return 0; }
if (tmpans > k) { puts("No"); return 0; }
puts("Yes");
return 0;
}