0809题解
T1 花海(flower)
30分
暴力枚举\(i_1,j_1,i_2,j_2\),暴力求和,时间复杂度\(O(n^5)\)
50分
暴力枚举\(i_1,j_1,i_2,j_2\),前缀和快速求和,时间复杂度\(O(n^4)\)
100分
不妨设\(n\le m\) ,那么\(n\le \sqrt {2\times 10^5}\)
枚举\(i_1,i_2,j_1\),我们需要求出\(j_2>j_1\)使得前缀和最大,这可以通过倒着枚举\(j_1\),更新\(j_2\)实现
总时间复杂度\(O(n ^2m)\)
T2 划分(split)
20分
暴力枚举
40分
令\(f_i\)表示\(1-i\)的划分的最大值和最小值
100分
优化dp
后面的约束条件可以通过单调队列解决
固定\(i\)
前面这部分,\(f_j\)是单调不降的,\(Max_{k=j}^{i}a_k\)是单调不增的
对于每一个不同的\(Max_{k=j}^{i}a_k\),我们发现\(f_{j-1}+Max_{k=j}^{i} a_k\)都在\(j\)最小值处取到最小值,而\(f_j\)不变
我们可以再开一个单调队列,来维护\(max_{k=j}^{i}a_k\)
然后对于每一个队列中的元素统计他们的最小值,就可以得到\(f_i\)
我们需要求最小值、修改元素、删除元素,可以用multiset维护
时间复杂度\(O(nlgn)\)
T3 落子无悔
30分
暴力搜索
100分
题目要求我们自顶向下删除,我们可以将这个过程反过来,自底向上合并
对于节点\(x\),其子节点为\(y_1,y_2,..,y_k\)假设已经合并出序列,我们将这些序列重组,对于顺序的决定,我们可以采用贪心,通过微扰法我们发现,我们需要将他们按照\(\frac{s_0(y)}{s_1(y)}\)从大到小排序即可,\(s_k(y)\)表示\(y\)的序列中\(k\)的数量
放眼全局,选了\(x\),接下来我们选\(x\)的儿子\(y_1\),我们不妨将\(x\)和\(y_1\)绑在一起,看作一个新的连通块
这样,每次我们按照\(\frac{s_0(y)}{s_1(y)}\)从大到小,将\(y\)合并到父亲\(x\)上
我们需要每次求出最小值、弹出最小值、对元素进行修改,所以我们可以采用堆或者平衡树
合并我们可以通过并查集实现。
时间复杂度\(O(nlgn+\alpha n)\)
题解比较抽象,还是看代码理解吧(感谢wsq大佬
点击查看代码
#include<cstdio>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int N=2e5+5;
int n,fa[N],f[N],a[N],siz[N],cnt1[N],cnt0[N];
ll ans;
struct node{
int id,x,y,t;
bool operator <(const node a)const{
return x*a.y<y*a.x;
}
};
int find(int x)
{
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
priority_queue<node> q;
int main()
{
freopen("board.in","r",stdin);
freopen("board.out","w",stdout);
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d",&fa[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[i]=i,siz[i]=1;
// if(i==1) continue;
if(a[i]) q.push((node){i,0,1,1}),cnt1[i]=1;
else q.push((node){i,1,0,1}),cnt0[i]=1;
}
while(!q.empty())
{
node tmp=q.top();
q.pop();
int x=find(tmp.id);
if(x==1||siz[x]!=tmp.t) continue;
int y=find(fa[x]);
// printf("%d %d\n",x,y);
ans+=1ll*cnt1[y]*cnt0[x];
siz[y]+=siz[x],cnt0[y]+=cnt0[x],cnt1[y]+=cnt1[x];
f[x]=y;
if(y!=1) q.push((node){y,cnt0[y],cnt1[y],siz[y]});
}
printf("%lld",ans);
return 0;
}
T4 游乐园
30分
暴力搜索
50分
如果每个右端点不一样,可以通过每次取右端点构造一个方案
反之
我们来构造一个右端点不一样的区间
对右端点从大到小排序,得到{r1,r2,……},对于相同的r,可以将他们变成{r,r-1,r-2……},这便得到了最优的右端点序列
一个很显然的想法,让左端点较小的区间有较小的右端点显然更优
然后开始分配右端点,按左端点从大到小排序后,每次取离r最近且<=r的右端点,顺便判Sorry
这可以用set实现
100分
先按50分方式处理
显然一定存在一个答案最小的区间每次选择的点都是右端点(调整法)
之后按右端点从小到大选择必须的
每次找出左端点小于等于当前右端点的全部区间,按不同的种类删去右端点最小的区间,可以用set维护
时间复杂度为 \(O(nlgn)\)
0827 T3
点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5;
const long long mod = 998244353;
const long long ny2 = 499122177;
int n, q, typ, dep[N + 5], siz[N + 5], f[N + 5][20];
long long ans = 0, sum = 0, dd[N + 5], ud[N + 5], st[N + 5][20], vd[N + 5][20], tu[N + 5][20];
vector<int> G[N + 5];
long long sq(int x) { return 1ll * x * x % mod; }
void dfs1(int u, int fa) {
f[u][0] = fa;
for (int i = 1; i <= 19; i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
}
dep[u] = dep[fa] + 1;
siz[u] = 1;
for (int v : G[u]) {
if (v == fa)
continue;
dfs1(v, u);
siz[u] += siz[v];
dd[u] = (dd[u] + dd[v] + siz[v]) % mod;
}
}
void dfs2(int u, long long sd) {
ud[u] = sd;
for (int i = 1; i <= 19; i++) {
st[u][i] = (st[u][i - 1] + st[f[u][i - 1]][i - 1]) % mod;
vd[u][i] = (vd[u][i - 1] + vd[f[u][i - 1]][i - 1]) % mod;
tu[u][i] = (tu[u][i - 1] + tu[f[u][i - 1]][i - 1]) % mod;
}
for (int v : G[u]) {
if (v == f[u][0])
continue;
tu[v][0] = 1ll * siz[v] * (n - siz[v]) % mod;
sum = (sum + tu[v][0]) % mod;
st[v][0] = sq(siz[u] - siz[v]);
vd[v][0] = (dd[u] - dd[v] - siz[v] + 2 * mod) % mod * (n - (siz[u] - siz[v])) % mod;
dfs2(v, (sd + dd[u] - dd[v] - siz[v] + (n - siz[v]) + 2 * mod) % mod);
}
}
int gtlca(int x, int y) {
if (dep[x] < dep[y])
swap(x, y);
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y])
x = f[x][i];
}
if (x == y)
return x;
for (int i = 19; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
}
long long gtsz2(int x, int y) {
long long re = (sq(siz[x]) + sq(siz[y])) % mod;
if (dep[x] < dep[y])
swap(x, y);
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y]) {
re = (re + st[x][i]) % mod;
x = f[x][i];
}
}
for (int i = 19; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
re = (re + st[x][i] + st[y][i]) % mod;
x = f[x][i];
y = f[y][i];
}
}
re = (re + sq(n - siz[x] - siz[y])) % mod;
return re;
}
long long gtd(int x, int y) {
long long re = (dd[x] * (n - siz[x]) + dd[y] * (n - siz[y])) % mod;
if (dep[x] < dep[y])
swap(x, y);
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y]) {
re = (re + vd[x][i]) % mod;
x = f[x][i];
}
}
for (int i = 19; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
re = (re + vd[x][i] + vd[y][i]) % mod;
x = f[x][i];
y = f[y][i];
}
}
int lca = f[x][0];
long long tmp = ud[lca] + dd[lca] - dd[x] - dd[y] - siz[x] - siz[y];
tmp = (tmp % mod + mod) % mod;
re = (re + tmp * (siz[x] + siz[y])) % mod;
return re;
}
long long gtmr(int x, int y) {
long long re = 0;
if (dep[x] < dep[y])
swap(x, y);
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] >= dep[y]) {
re = (re + tu[x][i]) % mod;
x = f[x][i];
}
}
if (x == y)
return re;
for (int i = 19; i >= 0; i--) {
if (f[x][i] != f[y][i]) {
re = (re + tu[x][i] + tu[y][i]) % mod;
x = f[x][i];
y = f[y][i];
}
}
re = (re + tu[x][0] + tu[y][0]) % mod;
return re;
}
long long gtlsz2(int x, int y) {
long long re = sq(siz[x]);
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] > dep[y]) {
re = (re + st[x][i]) % mod;
x = f[x][i];
}
}
re = (re + sq(n - siz[x])) % mod;
return re;
}
long long gtld(int x, int y) {
long long re = dd[x] * (n - siz[x]) % mod;
for (int i = 19; i >= 0; i--) {
if (dep[f[x][i]] > dep[y]) {
re = (re + vd[x][i]) % mod;
x = f[x][i];
}
}
long long tmp = ud[y] + dd[y] - dd[x] - siz[x];
tmp = (tmp % mod + mod) % mod;
re = (re + tmp * siz[x]) % mod;
return re;
}
void sol(int x, int y) {
if (x == y) {
ans = sum;
return;
}
if (dep[x] < dep[y])
swap(x, y);
int lca = gtlca(x, y);
int len = dep[x] + dep[y] - 2 * dep[lca] + 1;
if (y == lca) {
ans = 1ll * len * (1ll * n * n - gtlsz2(x, y)) % mod * ny2 % mod;
ans = (ans + sum - gtmr(x, y) + gtld(x, y) + mod) % mod;
} else {
ans = 1ll * len * (1ll * n * n - gtsz2(x, y)) % mod * ny2 % mod;
ans = (ans + sum - gtmr(x, y) + gtd(x, y) + mod) % mod;
}
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d%d%d", &n, &q, &typ);
for (int i = 1, x, y; i < n; i++) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(1, 0);
dfs2(1, 0);
while (q--) {
int x, y;
scanf("%d%d", &x, &y);
if (typ)
x ^= ans, y ^= ans;
sol(x, y);
printf("%lld\n", ans);
}
return 0;
}