2022NOIP联测7 10月11日

2022NOIP联测7 10月11日

题解做法:

欧几里得距离公式:

(xixj)2+(yiyj)2=xi2+yi2+xj2+yj22xixj2yiyj

直接预处理 xi2yi2xiyi O(1) 查询。

我的做法:

x 轴与 y 轴分开处理,两个互不影响,最后答案加起来。

先从小到大排序,处理 x1 的解,O(1) 转移下一个的答案,也是拆的平方差的式子,但有些麻烦。

Code moo~~
// 好像可以 O(1) 转移
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define re register int
#define pc_ putchar(' ')
#define pc_n putchar('\n')
#define Bessie int
const int MOD = 998244353, CTR = 2e5 + 7;
int n;
ll ansx[CTR], ansy[CTR], sumx[CTR], sumy[CTR], sumpre, sumend;
struct star
{
ll x, y;
int idx;
}a[CTR];
bool cmp1(star A, star B)
{
return A.x < B.x;
}
bool cmp2(star A, star B)
{
return A.y < B.y;
}
Bessie main()
{
n = read();
for(re i = 1; i <= n; ++i)
{
a[i].x = read(), a[i].y = read(), a[i].idx = i;
}
sort(a + 1, a + n + 1, cmp1);
for(re i = 1; i < n; ++i)
{
sumx[i] = a[i + 1].x - a[i].x;
sumx[i] += sumx[i - 1];
sumend = (sumend + sumx[i]) % MOD;
}
for(re i = 1; i < n; ++i)
{
ansx[a[1].idx] = (ansx[a[1].idx] + sumx[i] * sumx[i] % MOD) % MOD;
}
sumpre = 0;
// printf("pre:%lld end:%lld\n", sumpre, sumend);
for(re i = 2; i <= n; ++i)
{
sumend = (sumend - (sumx[i - 1] - sumx[i - 2]) * (n - i + 1) % MOD) % MOD;
ansx[a[i].idx] = (
ansx[a[i - 1].idx]
+ (i - 2 - (n - i)) * (sumx[i - 1] - sumx[i - 2]) % MOD * (sumx[i - 1] - sumx[i - 2]) % MOD
+ 2 * (sumx[i - 1] - sumx[i - 2]) % MOD * sumpre % MOD
- 2 * (sumx[i - 1] - sumx[i - 2]) % MOD * sumend % MOD
);
sumpre = (sumpre + (sumx[i - 1] - sumx[i - 2]) * (i - 1) % MOD) % MOD;
ansx[a[i].idx] = (ansx[a[i].idx] % MOD + MOD) % MOD;
// printf("pre:%lld end:%lld\n", sumpre, sumend);
}
sumend = 0;
sort(a + 1, a + n + 1, cmp2);
for(re i = 1; i < n; ++i)
{
sumy[i] = a[i + 1].y - a[i].y;
sumy[i] += sumy[i - 1];
sumend = (sumend + sumy[i]) % MOD;
}
for(re i = 1; i < n; ++i)
{
ansy[a[1].idx] = (ansy[a[1].idx] + sumy[i] * sumy[i] % MOD) % MOD;
}
sumpre = 0;
// printf("pre:%lld end:%lld\n", sumpre, sumend);
for(re i = 2; i <= n; ++i)
{
sumend = (sumend - (sumy[i - 1] - sumy[i - 2]) * (n - i + 1) % MOD) % MOD;
ansy[a[i].idx] = (
ansy[a[i - 1].idx]
+ (i - 2 - (n - i)) * (sumy[i - 1] - sumy[i - 2]) % MOD * (sumy[i - 1] - sumy[i - 2]) % MOD
+ 2 * (sumy[i - 1] - sumy[i - 2]) % MOD * sumpre % MOD
- 2 * (sumy[i - 1] - sumy[i - 2]) % MOD * sumend % MOD
);
sumpre = (sumpre + (sumy[i - 1] - sumy[i - 2]) * (i - 1) % MOD) % MOD;
ansy[a[i].idx] = (ansy[a[i].idx] % MOD + MOD) % MOD;
// printf("pre:%lld end:%lld\n", sumpre, sumend);
}
for(re i = 1; i <= n; ++i) {
printf("%lld\n", (ansx[i] + ansy[i]) % MOD);
}
return 0;
}

题解(树剖)

考虑树上两点间的距离为 dis(u,v)=depu+depv2deplca ,想到对于一个询问 u 来说,要找到最小的 depv2deplcav 是标记的黑点。

先树剖一下建线段树,每次修改时向上更新节点维护的 depv2depfa ,询问时也向上取最小值,直到根节点。

为什么是对的呢?

考虑所有修改与询问都向上跳时,他们最早一定在 lca 处相遇,由上面的距离式子可知此时的答案一定是最小的,即使跳过了,由于我们一直取 min ,也不会影响答案。

现在重点说一下线段树怎么维护。

维护两个值:minn2depfasumdepv2depfa ,一个标记(子树中所有黑点的深度) multisetS 。标记永久化。

建树:

minnp=2deprkl

修改:(设修改点为 k

1.白点变黑:

S 中加入 depksum=min(sum,minn+depk)

2.黑点变白:

S 中删 depk ,将 sum 重设为左右儿子 sum 的较小值(如果是叶子就设为 INF),再看一下子树中有没有黑点,sum 再更新一下取较小值。

push\_up 时不仅需要左右儿子的 sum ,还要考虑他们的标记和当前节点的标记。

询问:

返回 pair(sum,minn) ,因为标记永久化,每一次向上传时也要根据当前节点的标记再次计算答案。

每次在 multiset 中询问时先要看看是否为空!

无了。

对了,树剖别像我似的打错,指建树时不用 dfn ,要用 rk

Code moo~~
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define re register int
#define pc_ putchar(' ')
#define pc_n putchar('\n')
#define Bessie int
const int CTR = 1e5 + 7, INF = 0x7f7f7f7f;
int n, Q;
bool black[CTR];
int blacknum;
struct edge
{
int to, nxt;
}e[CTR << 1];
int h[CTR], etot;
void addedge(int x, int y)
{
e[++etot].to = y;
e[etot].nxt = h[x];
h[x] = etot;
}
int siz[CTR], dep[CTR], fa[CTR], son[CTR];
void dfs1(int x, int f)
{
fa[x] = f;
dep[x] = dep[f] + 1;
siz[x] = 1;
for(re i = h[x], v; i; i = e[i].nxt)
{
v = e[i].to;
if(v == f) continue;
dfs1(v, x);
siz[x] += siz[v];
if(siz[v] > siz[son[x]]) son[x] = v;
}
}
int top[CTR], dfn[CTR], rk[CTR], tme;
void dfs2(int x, int topf)
{
top[x] = topf;
dfn[x] = ++tme;
rk[tme] = x;
if(son[x]) dfs2(son[x], topf);
for(re i = h[x], v; i; i = e[i].nxt)
{
v = e[i].to;
if(v == fa[x] || v == son[x]) continue;
dfs2(v, v);
}
}
typedef pair<int, int> pii;
struct Tree
{
int minn[CTR << 2], sum[CTR << 2];
multiset<int> S[CTR << 2];
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
#define mid ((l + r) >> 1)
void built(int p, int l, int r)
{
sum[p] = INF;
if(l == r)
{
minn[p] = -2 * dep[rk[l]];
return ;
}
built(ls(p), l, mid), built(rs(p), mid + 1, r);
minn[p] = min(minn[ls(p)], minn[rs(p)]);
}
void update(int p, int l, int r, int L, int R, int k)
{
if(L <= l && r <= R)
{
if(black[k])
{
sum[p] = min(sum[p], minn[p] + dep[k]);
S[p].insert(dep[k]);
}
else
{
S[p].erase(S[p].find(dep[k]));
if(l != r)
{
sum[p] = min(sum[ls(p)], sum[rs(p)]);
}
else
{
sum[p] = INF;
}
if(!S[p].empty())
{
sum[p] = min(sum[p], minn[p] + *S[p].begin());
}
}
return;
}
if(L <= mid) update(ls(p), l, mid, L, R, k);
if(R > mid) update(rs(p), mid + 1, r, L, R, k);
int res = INF;
if(!S[p].empty()) res = minn[p] + *S[p].begin();
sum[p] = min({sum[ls(p)], sum[rs(p)], res});
}
pii query(int p, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return make_pair(sum[p], minn[p]);
}
pii A = { INF, INF }, B = { INF, INF };
int ans1 = INF, ans2 = INF, ans3 = INF, ans4 = INF;
if(L <= mid) A = query(ls(p), l, mid, L, R);
if(R > mid) B = query(rs(p), mid + 1, r, L, R);
ans1 = A.first, ans2 = B.first;
if(!S[p].empty()) ans3 = A.second + *S[p].begin(), ans4 = B.second + *S[p].begin();
return make_pair(min({ans1, ans2, ans3, ans4}), min(A.second, B.second));
}
}T;
void modify(int x, int y, int k)
{
if(dep[x] < dep[y]) swap(x, y);
while(top[x] != top[y])
{
T.update(1, 1, n, dfn[top[x]], dfn[x], k);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
T.update(1, 1, n, dfn[x], dfn[y], k);
}
int qmin(int x, int y)
{
int ans = INF;
if(dep[x] < dep[y]) swap(x, y);
while(top[x] != top[y])
{
ans = min(ans, T.query(1, 1, n, dfn[top[x]], dfn[x]).first);
x = fa[top[x]];
}
if(dep[x] > dep[y]) swap(x, y);
ans = min(ans, T.query(1, 1, n, dfn[x], dfn[y]).first);
return ans;
}
Bessie main()
{
n = read(), Q = read();
for(re i = 1, x, y; i < n; ++i)
{
x = read(), y = read();
addedge(x, y);
addedge(y, x);
}
dfs1(1, 1);
dfs2(1, 1);
T.built(1, 1, n);
int op, x;
while(Q--)
{
op = read(), x = read();
if(op == 1)
{
black[x] ^= 1;
modify(1, x, x);
if(black[x]) ++blacknum;
else --blacknum;
}
else
{
if(!blacknum) ot(-1),pc_n;
else ot(dep[x] + qmin(1, x)),pc_n;
}
}
return 0;
}

科技题。还不会,写题目名称仅供一乐。


题解(树形背包+剪枝)

理论复杂度 O(nm2) TLEcoders 数据水给过了。

dpi,j 为以 i 为根的子树选了 a 的容量为 j 时的 b 的最大值。

转移平凡。

一个剪枝就是要保证 a (容量)合法,不合法直接 continue

上下界剪枝就是不超过子树中 a 之和。

Code moo~~
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define re register int
#define pc_ putchar(' ')
#define pc_n putchar('\n')
#define Bessie int
const int CTR = 1e3 + 7;
int n, m;
int a[CTR], b[CTR];
int atot[CTR];
struct edge
{
int to, nxt;
}e[CTR * CTR];
int h[CTR], etot;
void addedge(int x, int y)
{
e[++etot].to = y;
e[etot].nxt = h[x];
h[x] = etot;
}
int ans;
int dp[CTR][CTR * 10];
void dfs1(int x, int f)
{
dp[x][a[x]] = max(dp[x][a[x]], b[x]);
atot[x] += a[x];
for(re i = h[x], v; i; i = e[i].nxt)
{
v = e[i].to;
if(v == f) continue;
dfs1(v, x);
for(re j = min(atot[x], m); j >= 0; --j)
{
if(dp[x][j] == -1) continue;
for(re k = min(atot[v], m - j); k >= 0; --k)
{
if(dp[v][k] == -1) continue;
dp[x][j + k] = max(dp[v][k] + dp[x][j], dp[x][j + k]);
ans = max(ans, dp[x][j + k]);
}
}
atot[x] += atot[v];
}
}
Bessie main()
{
memset(dp, -1, sizeof(dp));
n = read(), m = read();
for(re i = 1; i <= n; ++i)
{
a[i] = read(), b[i] = read();
}
for(re i = 1, x, y; i < n; ++i)
{
x = read(), y = read();
addedge(x, y);
addedge(y, x);
}
dfs1(1, 1);
ot(ans);
return 0;
}

总结:T1 想的太慢,思路不好打,T2 BFS 用的 STL::queue ,每次清空是 O(n) 的没有手写快,T3 暴力没来得及打,T4 还可以。

今个三道题一个正解都没打。


题面


hzoiCreator_157

posted @   Creator_157  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示