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\)的划分的最大值和最小值

\[f_i=Min f_{j-1}+Max_{k=j}^{i} a_k,\sum_{k=j}^{i}a_k\le m \]

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;
}

posted @ 2025-08-09 14:14  Aapwp  阅读(12)  评论(0)    收藏  举报
我给你一个没有信仰的人的忠诚