1001
1002
因为切的顺序不受影响,所以我们可以先切上下再切左右,那么每次切左右的时候贡献的数量就是交点数量,那么我们直接二维偏序就行。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <vector>
using namespace std;
#define SZ(x) ((int)(x).size())
struct lowbit_tree {
vector<int> T;
int n;
void init(int _n) {
n = _n;
T.resize(n + 1);
for (int i = 0; i <= n; ++i)
T[i] = 0;
}
void add(int x, int val) {
for (int i = x; i <= n; i += i & -i)
T[i] += val;
}
int sum(int x) {
int ret = 0;
if (x > n)
x = n;
for (int i = x; i > 0; i -= i & -i)
ret += T[i];
return ret;
}
int query(int l, int r) {
return sum(r) - sum(l - 1);
}
} t1, t2;
int const N = 100005;
vector<int> tL[N], tR[N], tU[N], tD[N];
struct point {
int x, y;
};
vector<point> b[4];
int n, m, K;
struct node {
int x, y;
char c;
} a[N];
int getID(char c) {
if (c == 'L')
return 2;
if (c == 'R')
return 3;
if (c == 'U')
return 0;
if (c == 'D')
return 1;
return -1;
}
int main() {
ios::sync_with_stdio(0);
int T;
cin >> T;
while (T--) {
auto detx = vector<int>(), dety = vector<int>();
cin >> n >> m >> K;
for (int i = 0; i < K; ++i) {
cin >> a[i].x >> a[i].y >> a[i].c;
detx.push_back(a[i].x);
dety.push_back(a[i].y);
}
sort(detx.begin(), detx.end());
sort(dety.begin(), dety.end());
detx.erase(unique(detx.begin(), detx.end()), detx.end());
dety.erase(unique(dety.begin(), dety.end()), dety.end());
for (int i = 0; i < 4; ++i)
b[i].clear();
for (int i = 0; i <= SZ(detx); ++i) {
tU[i].clear();
tD[i].clear();
tL[i].clear();
tR[i].clear();
}
for (int i = 0; i < K; ++i) {
a[i].x = lower_bound(detx.begin(), detx.end(), a[i].x) - detx.begin() + 1;
a[i].y = lower_bound(dety.begin(), dety.end(), a[i].y) - dety.begin() + 1;
b[getID(a[i].c)].push_back({ a[i].x, a[i].y });
}
for (auto p : b[0])
tU[p.x].push_back(p.y);
for (auto p : b[1])
tD[p.x].push_back(p.y);
for (auto p : b[2])
tL[p.x].push_back(p.y);
for (auto p : b[3])
tR[p.x].push_back(p.y);
int ans = 0;
t1.init(SZ(dety));
t2.init(SZ(dety));
for (int i = SZ(detx); i >= 1; --i) {
for (auto& y : tU[i])
t1.add(y, 1);
for (auto& y : tD[i])
t2.add(y, 1);
for (auto& y : tR[i])
ans += t2.query(y, SZ(dety)) + t1.query(1, y);
}
t1.init(SZ(dety));
t2.init(SZ(dety));
for (int i = 1; i <= SZ(detx); ++i) {
for (auto& y : tU[i])
t1.add(y, 1);
for (auto& y : tD[i])
t2.add(y, 1);
for (auto& y : tL[i])
ans += t2.query(y, SZ(dety)) + t1.query(1, y);
}
cout << ans + 1 << '\n';
}
}
1003
如果这题不被卡常(((((这场比赛也不会打的这么惨,因为是单人打,所以内心一直都是不AC或者假了不逃x,然后就卡了一下午的常数。
首先肯定是一个meet in middle
你就考虑每一位的贡献,因为和的大小是一个9位数,我们分别考虑其中的每一位,假如我们枚举左边的和的这一位,那么分开讨论进不进位就可以得到右边的这一位应该是什么。
然后大力统计就行。
当天发现sort比lower_bound快2到3倍,那么就把统计答案离线下来sort,而且如果最开始的时候位数比较小,那么可以利用桶来计算答案,于是5s过了x
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <ctime>
using namespace std;
int sum[2][2100000];
int vec[10][2100000];
int top[10];
pair<int, bool> que[10][2100000];
int q[10];
int a[50];
int n;
int pres[10][100005];
void dfs(int k, int lim, int s, int t) {
if (k > lim) {
sum[t][++sum[t][0]] = s;
return;
}
dfs(k + 1, lim, s + a[k], t);
dfs(k + 1, lim, s, t);
}
int main() {
ios::sync_with_stdio(0);
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
int mid = (1 + n) >> 1;
sum[0][0] = sum[1][0] = 0;
if (n >= 30)
--mid;
if (n & 1)
--mid;
dfs(1, mid, 0, 0);
dfs(mid + 1, n, 0, 1);
int mod = 1;
long long ans = 0;
for (int i = 1; i <= 9; ++i, mod *= 10) {
for (int j = 0; j < 10; ++j) {
top[j] = 0;
q[j] = 0;
}
if (i <= 6) {
for (int j = 0; j <= 9; ++j)
for (int k = 0; k < mod; ++k)
pres[j][k] = 0;
for (int j = 1; j <= sum[1][0]; ++j) {
int cnt = (sum[1][j] / mod) % 10;
pres[cnt][sum[1][j] % mod]++;
// cerr << "> " << sum[1][j] << ' ' << cnt << ' ' << sum[1][j] % mod << '\n';
}
for (int j = 0; j <= 9; ++j)
for (int k = 1; k < mod; ++k)
pres[j][k] = pres[j][k - 1] + pres[j][k];
int t, r, c, bjw_c, jw_c;
for (int j = 1; j <= sum[0][0]; ++j) {
t = sum[0][j] / mod % 10;
r = sum[0][j] % mod;
c = (4 - t) < 0 ? 14 - t : 4 - t;
bjw_c = mod - r - 1;
ans += pres[c][bjw_c];
// cerr << "< ? " << i << ' ' << sum[0][j] << ' ' << c << ' ' << bjw_c << ' ' << pres[c][bjw_c] << '\n';
if (i > 1) {
jw_c = mod - r;
if (jw_c >= mod)
continue;
--c;
if (c < 0)
c += 10;
int tmp = pres[c][mod - 1] - (jw_c == 0 ? 0 : pres[c][jw_c - 1]);
ans += tmp;
// cerr << "< ?? " << i << ' ' << sum[0][j] << ' ' << c << ' ' << jw_c << ' ' << tmp << '\n';
}
}
} else {
for (int j = 1; j <= sum[1][0]; ++j) {
int cnt = (sum[1][j] / mod) % 10;
vec[cnt][top[cnt]++] = sum[1][j] % mod;
}
for (int j = 0; j < 10; ++j)
sort(vec[j], vec[j] + top[j]);
int t, r, c, bjw_c, jw_c;
for (int j = 1; j <= sum[0][0]; ++j) {
t = sum[0][j] / mod % 10;
r = sum[0][j] % mod;
c = (4 - t) < 0 ? 14 - t : 4 - t;
bjw_c = mod - r - 1;
que[c][q[c]++] = { bjw_c, 0 };
if (i > 1) {
jw_c = mod - r;
--c;
if (c < 0)
c += 10;
que[c][q[c]++] = { jw_c - 1, 1 };
}
}
for (int j = 0; j < 10; ++j)
sort(que[j], que[j] + q[j]);
for (int j = 0; j < 10; ++j) {
int p = 0;
for (int k = 0; k < q[j]; ++k) {
while (p < top[j] && vec[j][p] <= que[j][k].first)
++p;
ans += que[j][k].second ? top[j] - p : p;
}
}
}
}
cout << ans << '\n';
}
}
1004
考虑等比数列的首项设为
p
p
p,尾项设为
q
q
q,公比为
a
b
\frac{a}{b}
ba,
a
>
b
a > b
a>b且
g
c
d
(
a
,
b
)
=
=
1
gcd(a, b) == 1
gcd(a,b)==1,那么就有
p
∗
a
k
−
1
b
k
−
1
=
q
p*\frac{a^{k-1}}{b^{k-1}}=q
p∗bk−1ak−1=q,可以得到
q
∣
a
k
−
1
q | a^{k - 1}
q∣ak−1
那么答案很显然就是
∑
a
=
2
n
∑
k
=
1
63
φ
(
a
)
[
n
a
k
−
1
]
\sum_{a = 2}^n \sum_{k = 1}^{63} \varphi(a) [\frac{n}{a^{k - 1}}]
a=2∑nk=1∑63φ(a)[ak−1n]
那么你把
k
=
1
k = 1
k=1和
k
=
2
k = 2
k=2的情况拉出来,显然就是
n
∗
(
n
+
1
)
2
\frac{n*(n + 1)}{2}
2n∗(n+1)个
然后对于
k
=
3
k = 3
k=3的大力分块杜教筛,更大的就直接暴力算就完事了
大力分块杜教筛那块jls说是
O
(
n
5
12
)
O(n^{\frac{5}{12}})
O(n125)的,貌似我记得确实是这样的x,好像这个复杂度的我还做过一题,预处理一下可以到
O
(
n
1
3
)
O(n^{\frac{1}{3}})
O(n31)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <unordered_map>
using namespace std;
using i128 = __int128;
using ll = long long;
int const mod = 998244353;
ll KSM(ll a, ll k) {
ll ret = 1;
for (; k; k >>= 1, a = a * a % mod)
if (k & 1)
ret = ret * a % mod;
return ret;
}
i128 fp(i128 a, ll k) {
i128 ret = 1;
for (; k; k >>= 1, a = a * a)
if (k & 1)
ret = ret * a;
return ret;
}
int const M = 50000000;
int const N = M + 5;
int phi[N], pri[M / 5], tot;
int s[N];
bool isp[N];
ll inv2;
void SAI() {
phi[1] = 1;
isp[1] = 1;
for (int i = 2; i <= M; ++i) {
if (isp[i] == 0) {
pri[++tot] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j])
phi[i * pri[j]] = phi[i] * phi[pri[j]];
else {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
for (int i = 1; i <= M; ++i)
s[i] = (s[i - 1] + phi[i]) % mod;
}
unordered_map<ll, int> mp;
ll sum_(ll x) {
if (x <= M)
return s[x];
if (mp.count(x))
return mp[x];
ll ans = (x % mod) * ((x + 1) % mod) % mod * inv2 % mod;
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ans = (ans - (r - l + 1) % mod * sum_(x / l) % mod) % mod;
}
ans = (ans + mod) % mod;
mp[x] = ans;
return ans;
}
ll sqrt_(ll x) {
ll ret = sqrt(x);
while (ret * ret < x)
++ret;
while (ret * ret > x)
--ret;
return ret;
}
int main() {
SAI();
int T;
cin >> T;
inv2 = KSM(2, mod - 2);
while (T--) {
ll n;
cin >> n;
ll ans = (n % mod) * ((n + 1) % mod) % mod * inv2 % mod;
i128 mi2 = 4;
for (int i = 3; mi2 <= n / 2; ++i, mi2 <<= 1) {
for (ll a = 2; a <= n; ++a) {
i128 t = fp(a, i);
if (t > n)
break;
ans = (ans + phi[a] * ((n / t) % mod) % mod) % mod;
}
}
for (ll l = 2, r; l * l <= n; l = r + 1) {
r = sqrt_(n / (n / l / l));
ans = (ans + n / l / l % mod * (sum_(r) - sum_(l - 1) + mod) % mod) % mod;
}
cout << ans << '\n';
}
}
1005
比较有趣的讨论题
首先如果某个位置是z肯定第一个人会点掉,然后考虑第二个人如何。
因为第二个人可以立即结束游戏,所以如果这个人不点第一个z,那么可以直接结束游戏,避免情况变差。
但是如果从开头到第一个z中间有非y的字符,那么第一个人不可能增大,于是应该是结束游戏。
否则就是增大第一个z,第二个人结束游戏。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 110;
char s[maxn];
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%s",s);
int p = 0;
while(s[p] == 'y') p++;
if(s[p] == 'z') s[p] = 'b';
printf("%s\n",s);
}
return 0;
}
1006
首先100的数量一定是最大的钱数/100或者是这个数量-1
那么其他的钱一定可以爆枚来chk
但是有个坑,比如有可能某个人去掉若干100后剩下的10实际上可以当做凑110使用,于是要特判一下。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
bool chk(int x, int y, int z, int v) {
for (int i = 0; i <= x; ++i)
for (int j = 0; j <= y; ++j)
for (int k = 0; k <= z; ++k)
if (i + 2ll * j + 5ll * k == v)
return 1;
return 0;
}
int solve(vector<pair<int, int>>& a) {
int ret = 10000000;
for (int i = 0; i <= 7; ++i)
for (int j = 0; j <= 6; ++j)
for (int k = 0; k <= 4; ++k) {
int flag = 1;
for (auto& p : a) {
if (chk(i, j, k, p.first) == 1)
continue;
if (p.second && chk(i, j, k, 10 + p.first))
continue;
flag = 0;
break;
}
if (flag)
ret = min(ret, i + j + k);
}
return ret;
}
int solve(vector<int>& a) {
int ret = 10000000;
for (int i = 0; i <= 7; ++i)
for (int j = 0; j <= 6; ++j)
for (int k = 0; k <= 4; ++k) {
int flag = 1;
for (auto& v : a)
if (chk(i, j, k, v) == 0) {
flag = 0;
break;
}
if (flag)
ret = min(ret, i + j + k);
}
return ret;
}
int main() {
ios::sync_with_stdio(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
auto a = vector<int>(n);
int flag = 1;
for (auto& v : a) {
cin >> v;
if (v % 10)
flag = 0;
v /= 10;
}
if (flag == 0) {
cout << -1 << '\n';
continue;
}
int ans = 2e9;
{
auto b = vector<pair<int, int>>();
for (auto& v : a)
b.emplace_back(v, 0);
int anss = 0;
for (auto& p : b)
anss = max(anss, p.first / 10 - 1);
for (auto& p : b) {
int& v = p.first;
p.second = anss && (v / 10 <= anss) && v / 10;
v = (v / 10 <= anss ? v % 10 : (v % 10 + 10));
}
anss += solve(b);
ans = min(ans, anss);
}
{
auto b = a;
int anss = 0;
for (auto& v : b) {
anss = max(anss, v / 10);
v = v % 10;
}
anss += solve(b);
ans = min(ans, anss);
}
cout << ans << '\n';
}
}
1007
有两种做法?x
我们维护一个答案数组表示如果元组的第一个数为x的时候,第二个数最大能是多少
第一种是如果去掉一条边,那么肯定会分成两个子树,那么不相交的路径肯定是两个子树里面分别的路径,那么每次统计左右的子树的直径,然后更新答案数组就行,这里需要统计左右子树的直径的话,需要树DP
第二种是这两条路径一定可以通过移动,使得要么一条完全在直径上,另一条在直径外,要么两条分别占用直径的一个端点。那么维护一下直径上每个点都能往四周延伸多远,然后统计一下就可以做第二种情况了,对于第一种情况只需要直接把直径删掉统计一下每个子树的直径长度即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define SZ(x) ((int)(x).size())
using ll = long long;
inline ll read() {
ll d = 0;
char s = getchar();
while (s < '0' || s > '9')
s = getchar();
while ('0' <= s && s <= '9') {
d = d * 10 + s - '0';
s = getchar();
}
return d;
}
int const N = 100005;
struct edge {
int y, next;
} e[N << 1];
int last[N], ne;
void addedge(int x, int y) {
e[++ne].y = y;
e[ne].next = last[x];
last[x] = ne;
}
int ans[N];
int n;
int f[N], pre[N], suf[N];
int dis[N], pr[N];
queue<int> q;
vector<int> get_mean(int S, vector<int>& vis) {
q.push(S);
vis[S] = 1;
dis[S] = 0;
int mx = 0, id1 = S, id2 = -1;
while (q.empty() == 0) {
int now = q.front();
q.pop();
for (int i = last[now]; i; i = e[i].next) {
if (vis[e[i].y] != 0)
continue;
vis[e[i].y] = 1;
dis[e[i].y] = dis[now] + 1;
if (dis[e[i].y] > mx) {
mx = dis[e[i].y];
id1 = e[i].y;
}
q.push(e[i].y);
}
}
q.push(id1);
mx = 0;
vis[id1] = 2;
dis[id1] = 0;
pr[id1] = id1;
id2 = id1;
while (q.empty() == 0) {
int now = q.front();
q.pop();
for (int i = last[now]; i; i = e[i].next) {
if (vis[e[i].y] != 1)
continue;
vis[e[i].y] = 2;
dis[e[i].y] = dis[now] + 1;
pr[e[i].y] = now;
q.push(e[i].y);
if (dis[e[i].y] > mx) {
mx = dis[e[i].y];
id2 = e[i].y;
}
}
}
auto ret = vector<int>();
int x = id2;
ret.push_back(x);
while (pr[x] != x) {
x = pr[x];
ret.push_back(x);
}
return ret;
}
int main() {
int T = read();
while (T--) {
n = read();
for (int i = 1; i <= n; ++i)
last[i] = ans[i] = 0;
ne = 0;
for (int i = 1; i < n; ++i) {
int x = read(), y = read();
addedge(x, y);
addedge(y, x);
}
auto vis = vector<int>(n + 1, 0);
auto mean_all = get_mean(1, vis);
auto mark_mean = vector<int>(n + 1, 0);
for (auto y : mean_all)
mark_mean[y] = -1;
fill(vis.begin(), vis.end(), 0);
int len = 0;
for (int i = 0; i < SZ(mean_all); ++i) {
int x = mean_all[i];
dis[x] = 0;
vis[x] = 1;
q.push(x);
f[i] = 0;
while (q.empty() == 0) {
int now = q.front();
q.pop();
for (int j = last[now]; j; j = e[j].next) {
if (mark_mean[e[j].y] || vis[e[j].y] != 0)
continue;
vis[e[j].y] = 1;
dis[e[j].y] = dis[now] + 1;
f[i] = max(f[i], dis[e[j].y]);
q.push(e[j].y);
}
}
pre[i] = suf[i] = f[i];
}
for (int i = 1; i <= n; ++i)
if (mark_mean[i] == 0) {
int t = SZ(get_mean(i, mark_mean));
len = max(len, t);
}
for (int i = 0; i < SZ(mean_all); ++i)
pre[i] = max(pre[i] + i + 1, i == 0 ? 0 : pre[i - 1]);
for (int i = SZ(mean_all) - 1, j = 0; i >= 0; --i, ++j)
suf[i] = max(suf[i] + j + 1, j == 0 ? 0 : suf[i + 1]);
for (int i = 0; i < SZ(mean_all) - 1; ++i) {
int lef = pre[i], rig = suf[i + 1];
ans[lef] = max(ans[lef], rig);
ans[rig] = max(ans[rig], lef);
}
ans[len] = max(ans[len], SZ(mean_all));
ans[SZ(mean_all)] = max(ans[SZ(mean_all)], len);
for (int i = n - 1; i >= 1; --i)
ans[i] = max(ans[i + 1], ans[i]);
ll anss = 0;
for (int i = 1; i <= n; ++i)
anss += ans[i];
cout << anss << '\n';
}
}
1008
为何傻逼题我被1003卡着结果没看到1551
傻逼trie合并题
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
int const N = 100005;
int bit2[49];
struct trie_node {
int lc, rc, s, w;
} t[N * 70];
int cnt;
long long ans;
inline void pushup(int k) {
t[k].s = t[t[k].lc].s + t[t[k].rc].s;
}
void insert(int&k, int val, int dep) {
if (k == 0) {
k = ++cnt;
t[k] = { 0, 0, 0, 0 };
}
if (dep < 0) {
t[k].s++;
t[k].w = val;
return;
}
int mid = (val & bit2[dep]);
if (mid == 0)
insert(t[k].lc, val, dep - 1);
else
insert(t[k].rc, val, dep - 1);
pushup(k);
}
void merge(int k1, int k2, int dep) {
if (t[k1].s == 0 || t[k2].s == 0)
return;
if (dep < 0) {
while (t[k1].s && t[k2].s) {
ans += (t[k1].w ^ t[k2].w);
t[k1].s--;
t[k2].s--;
}
return;
}
merge(t[k1].lc, t[k2].rc, dep - 1);
pushup(k1);
pushup(k2);
merge(t[k1].rc, t[k2].lc, dep - 1);
pushup(k1);
pushup(k2);
merge(t[k1].lc, t[k2].lc, dep - 1);
pushup(k1);
pushup(k2);
merge(t[k1].rc, t[k2].rc, dep - 1);
pushup(k1);
pushup(k2);
}
int main() {
ios::sync_with_stdio(0);
for (int i = 0; i <= 30; ++i)
bit2[i] = 1 << i;
int T;
cin >> T;
while (T--) {
int n, root1 = 0, root2 = 0;
cin >> n;
cnt = 0;
for (int i = 0; i < n; ++i) {
int x;
cin >> x;
insert(root1, x, 30);
}
for (int i = 0; i < n; ++i) {
int x;
cin >> x;
insert(root2, x, 30);
}
ans = 0;
merge(root1, root2, 30);
cout << ans << '\n';
}
}