20240624
T1
NFLSOJ P955 数字收藏
首先除掉
代码
#include <iostream>
#include <vector>
#define int long long
using namespace std;
const int N = 100000;
int n, K;
bool pr[100005];
int p[100005], pcnt;
int pnum[100005];
void Sieve(int n) {
for (int i = 2; i <= n; i++) {
if (!pr[i])
p[++pcnt] = i, pnum[i] = 1;
for (int j = 1; j <= pcnt && 1ll * p[j] * i <= n; j++) {
pr[p[j] * i] = 1;
pnum[p[j] * i] = pnum[i] + 1;
if (i % p[j] == 0)
break;
}
}
}
vector<int> pfac[100005];
vector<int> fct[100005];
vector<int> asdf[100005];
int ap[100005];
int cm[100005];
int ans = 0;
int ncnt;
void Add(int x, int y) {
if (x % K != 0)
return;
x /= K;
if (y < 0) {
for (auto v : fct[x])
cm[v] += y;
}
int tmp = 0;
for (auto v : asdf[x]) {
if (pnum[v] & 1)
tmp -= cm[v];
else
tmp += cm[v];
}
ans += tmp * y;
if (y > 0) {
for (auto v : fct[x])
cm[v] += y;
}
}
signed main() {
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
cin >> n >> K;
Sieve(100000);
for (int i = 1; i <= pcnt; i++) {
for (int j = 1; p[i] * j <= N; j++)
pfac[p[i] * j].emplace_back(p[i]);
}
for (int i = 1; i <= N; i++) {
int s = pfac[i].size();
int S = (1 << s) - 1;
for (int j = 0; j <= S; j++) {
int tmp = 1;
for (int k = 0; k < s; k++) {
if (j & (1 << k))
tmp *= pfac[i][k];
}
asdf[i].emplace_back(tmp);
}
for (int j = 1; j * j <= i; j++) {
if (i % j == 0) {
fct[i].emplace_back(j);
if (j * j != i)
fct[i].emplace_back(i / j);
}
}
}
for (int i = 1; i <= n; i++) {
int x, y;
cin >> x >> y;
if (x == 0) {
if (ap[y]) {
Add(y, -1);
--ap[y];
}
} else {
++ap[y];
Add(y, 1);
}
cout << ans << "\n";
}
return 0;
}
T2
NFLSOJ P2697 树形猜猜看
先考虑随一个点,把这个点对所有点问一遍。容易发现这样可以确定这个点到根的路径上所有点及其子树内有哪些点。这样就可以递归,但是复杂度不是很保证。如果每次都能随到叶子,则可以证明询问次数:设
代码
#include <bits/stdc++.h>
using namespace std;
random_device rd;
mt19937 mtrand(rd());
int n;
bool mark[100005];
int query(int x, int y) {
cout << "? " << x << " " << y << endl;
int ret;
cin >> ret;
return ret;
}
int ans[1050];
int work(int x, vector<int> vec) {
if (vec.size() == 1)
return x;
int stk[1050], sz = 0;
vector<int> sbt[1050];
int cur = x;
stk[++sz] = x;
for (auto v : vec) {
if (v == x)
continue;
int tmp = query(cur, v);
if (tmp == cur) {
stk[++sz] = v;
cur = v;
continue;
}
stk[++sz] = tmp;
if (tmp != v)
sbt[tmp].emplace_back(v);
}
sort(stk + 1, stk + sz + 1, [&](int a, int b) { return sbt[a].size() < sbt[b].size(); });
sz = unique(stk + 1, stk + sz + 1) - stk - 1;
for (int i = 1; i < sz; i++) ans[stk[i]] = stk[i + 1];
for (int i = 1; i <= sz; i++) {
if (sbt[stk[i]].size())
ans[work(sbt[stk[i]][0], sbt[stk[i]])] = stk[i];
}
return stk[sz];
}
vector<int> asdf;
int main() {
// freopen("B.in", "r", stdin);
// freopen("B.out", "w", stdout);
cin >> n;
for (int i = 1; i < (1 << n); i++) asdf.emplace_back(i);
ans[work(1, asdf)] = -1;
cout << "! ";
for (int i = 1; i < (1 << n); i++) cout << ans[i] << " ";
cout << endl;
return 0;
}
T3
NFLSOJ P2699 修得大树千万
先考虑对于一个点
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#define int long long
using namespace std;
inline char nnc(){
static char buf[1000005],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000005,stdin),p1==p2)?EOF:*p1++;
}
inline int read() {
int ret = 0;
char c = nnc();
while (c < '0' || c > '9') c = nnc();
while ('0' <= c && c <= '9') ret = ret * 10 + c - 48, c = nnc();
return ret;
}
int n;
int a[1000005];
vector<int> vec[1000005];
vector<int> pre[1000005];
vector<int> suf[1000005];
int mn[1000005];
vector<int> val[1000005];
vector<int> son[1000005];
void dfs1(int x, int fa) {
mn[x] = a[x];
for (auto v : vec[x]) {
if (v != fa) {
dfs1(v, x);
son[x].emplace_back(v);
mn[x] = min(mn[x], mn[v]);
val[x].emplace_back(mn[v]);
pre[x].emplace_back(mn[v]);
suf[x].emplace_back(mn[v]);
}
}
for (int i = 1; i < (int)pre[x].size(); i++) pre[x][i] = min(pre[x][i], pre[x][i - 1]);
for (int i = (int)suf[x].size() - 2; i >= 0; i--) suf[x][i] = min(suf[x][i], suf[x][i + 1]);
}
void dfs2(int x, int fa, int val) {
if (val <= n)
::val[x].emplace_back(val);
val = min(val, a[x]);
for (int i = 0; i < (int)son[x].size(); i++) {
int v = son[x][i];
if (son[x].size() == 1)
dfs2(v, x, val);
else {
int tmp = 2147483647;
if (i == 0)
tmp = suf[x][i + 1];
else if (i == (int)son[x].size() - 1)
tmp = pre[x][i - 1];
else
tmp = min(pre[x][i - 1], suf[x][i + 1]);
dfs2(v, x, min(val, tmp));
}
}
}
int stk[1000005], sz;
int deg[1000005];
multiset<int> st;
int calc(int x) {
if (deg[x] <= 1)
return 0;
int ret = 0;
sz = 0;
st.erase(st.find(a[x]));
for (auto v : val[x]) {
stk[++sz] = v;
ret += v;
st.erase(st.find(v));
st.insert(v + 1);
}
int cnt = deg[x] - 2;
while (cnt--) {
int v = *st.begin();
ret += v;
stk[++sz] = v;
st.erase(st.begin());
st.insert(v + 1);
}
st.insert(a[x]);
for (int i = sz; i; i--) {
st.erase(st.find(stk[i] + 1));
st.insert(stk[i]);
}
return ret;
}
signed main() {
// freopen("C.in", "r", stdin);
// freopen("C.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++) st.insert(a[i] = read());
for (int i = 1; i < n; i++) {
int u, v;
u = read(), v = read();
vec[u].emplace_back(v);
vec[v].emplace_back(u);
++deg[u], ++deg[v];
}
dfs1(1, 0);
dfs2(1, 0, 2147483647);
for (int i = 1; i <= n; i++) cout << calc(i) << " ";
cout << "\n";
return 0;
}
T4
NFLSOJ P5353 公益
先把所有同学按开始时间排序。如果把每个人吃的时间在数轴上标出,那么可以发现在食物补给前被杀死的一定是从补给时刻向前推的一段区间内的人。由于小 X 不能死,所以若排序后两个相邻的数是
代码
#include <iostream>
#include <algorithm>
#define int __int128
using namespace std;
const int inf = 2147483647;
long long T, n, m, w, t;
struct Person {
long long x, c;
} a[200005];
struct Merchant {
long long S, v;
} b[200005];
int Min[200005];
struct Line {
int k, b;
int operator()(int x) { return k * x + b; }
} stk[200005];
int f[200005], S[200005];
int sz;
bool chk(Line a, Line b, Line c) { return (1.0 * b.b - a.b) * (b.k - c.k) >= (1.0 * c.b - b.b) * (a.k - b.k); }
int Search(int x) {
int l = 1, r = sz - 1, ans = sz, mid;
while (l <= r) {
mid = (l + r) >> 1;
if (stk[mid + 1](x) >= stk[mid](x))
ans = mid, r = mid - 1;
else
l = mid + 1;
}
return ans;
}
signed main() {
// freopen("D.in", "r", stdin);
// freopen("D.out", "w", stdout);
cin >> T >> n >> m >> w >> t;
for (int i = 1; i <= n; i++) cin >> b[i].S, b[i].v = b[i].S / t, b[i].S %= t;
b[++n] = (Merchant) { T % t, T / t };
for (int i = 1; i <= m; i++) cin >> a[i].x >> a[i].c;
for (int i = 1; i <= m; i++) Min[i] = inf;
sort(a + 1, a + m + 1, [](Person a, Person b) { return a.x < b.x; });
sort(b + 1, b + n + 1, [](Merchant a, Merchant b) { return a.S < b.S; });
a[m + 1].x = inf;
for (int i = 1, j = 1; i <= m; i++) {
S[i] = S[i - 1] + a[i].c;
while (j <= n && b[j].S <= a[i].x) ++j;
while (j <= n && b[j].S < a[i + 1].x) Min[i] = min(Min[i], (int)b[j].v), ++j;
}
stk[++sz] = (Line) { 0, 0 };
for (int i = 1; i <= m; i++) {
f[i] = f[i - 1] + w * ((T - a[i].x) / t + 1);
if (Min[i] != inf) {
int x = w * Min[i];
int t = Search(x);
f[i] = min(f[i], stk[t](x) + w * Min[i] * i + S[i]);
}
Line tmp = (Line) { -i, f[i] - S[i] };
while (sz > 1 && chk(stk[sz - 1], stk[sz], tmp)) --sz;
stk[++sz] = tmp;
}
cout << (long long)(f[m] + w * (T / t + 1)) << "\n";
return 0;
}
连边使连通有时可以转化为选出一些点的方案,使得每个连通块中都至少有一个点被选中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步