ABC 406

ABC 406

C ~

本来只想整理EF题的,但好像C有难度,于是一块整理

(这个题目也挺魔性)

其实就是关于山峰山谷的问题

考虑将山峰和山谷先求出来有哪些

然后可以对每个点求贡献

代码:

E Popcount Sum 3

距离正解最近的一次,但没调出来

发现数据范围巨大,想到数位DP

说句实话,这不是数位DP板子吗

但我不会写/ll

这个题可以当做很好的数位DP的练习题

设f[i][j][k]表示考虑二进制下前i位,选了j个1,是否紧贴上界n

直接从高位往低位转移即可

具体看代码实现

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int MOD=998244353;
int dp[65][65][2],sum[65][65][2];
int cnt,ans[65];
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int T=read();
	while(T--){
		memset(dp,0,sizeof(dp));
		memset(sum,0,sizeof(sum));
		int n=read(),x=read();
		cnt=0;
		while(n>0){
			ans[++cnt]=n%2;
			n/=2;
		}
		dp[cnt+1][x][1]=1;
		for(int i=cnt;i>=1;i--){
			for(int j=0;j<=x;j++){
				for(int k=0;k<2;k++){
					int maxx=0;
					if(k==1) maxx=ans[i];
					else maxx=1;
					for(int q=0;q<=maxx;q++){
						int now=0;
						if(k&&q==maxx) now=1;
						int nw=j;
						if(q==1) nw--;
						if(nw<0) continue;
						jiaa(dp[i][nw][now],dp[i+1][j][k]);
						jiaa(sum[i][nw][now],sum[i+1][j][k]);
						if(q==1){
							int summ=((1LL<<(i-1))%MOD)*dp[i+1][j][k]%MOD;
							jiaa(sum[i][nw][now],summ);
						}
					}
				}
			}
		}
		cout<<(sum[1][0][1]+sum[1][0][0])%MOD<<'\n';
	}
	return 0;
}

注意运算符优先级!!!
注意位运算时<<需要1LL!!!

F - Compare Tree Weights

首先观察发现,断开一条边后,答案为整棵树的权值和减一颗子树的权值和

现在题目变成

1.单点加权值

2.快速求子树和

好像会树链剖分的到这里就秒了

但我不会啊

所以有一个重要的性质:

在dfs序下,子树上所有点是连续的

所以变成单点加区间求和

树状数组!!!

做完了!

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
struct node
{//树状数组
	int n;
	vector<ll> f;
	node(int _n): n(_n), f(n + 1, 0) {}
	void update(int i, ll v)
	{
		for(; i <= n; i += i & -i)
			f[i] += v;
	}
	ll qry(int i)
	{
		ll s = 0;
		for(; i > 0; i -= i & -i)
			s += f[i];
		return s;
	}
	ll qry2(int l, int r)
	{
		return qry(r) - qry(l - 1);
	}
}tree(300001);
vector<vector<pair<int, int> > > e(300001);
vector<pair<int, int> > ed(300001);
int aa[300001],tou[300001],pa[300001],son[300001];
vector<pair<int, int> > st;
int main()
{
	int n;
	cin >> n;
	for(int i = 1; i <= n - 1; i++)
	{
		int u, v;
		cin >> u >> v;
		ed[i] = {u, v};
		e[u].push_back({v, i});
		e[v].push_back({u, i});
	}
	int nw = 0;
	st.reserve(n * 2);
	st.emplace_back(1, 0);
	pa[1] = 0;
	while(!st.empty())//处理dfs序
	{
		int u = st.back().first;
		int &ci = st.back().second;
		if(ci == 0)
		{
			aa[u] = ++nw;
		}
		if(ci < (int)e[u].size())
		{
			auto [v, ei] = e[u][ci++];
			if(v == pa[u])
				continue;
			pa[v] = u;
			son[ei] = v;
			st.emplace_back(v, 0);
		}
		else
		{
			tou[u] = nw;
			st.pop_back();
		}
	}
	for(int i = 1; i <= n; i++)
	{
		tree.update(aa[i], 1);
	}
	int T;
	cin >> T;
	while(T--)
	{
		int op;
		cin >> op;
		if(op == 1)
		{
			int x;
			ll w;
			cin >> x >> w;
			tree.update(aa[x], w);//单点加
		}
		else if(op == 2)
		{
			int y;
			cin >> y;
			int c = son[y];
			ll s1 = tree.qry2(aa[c], tou[c]);//区间求和
			ll s2 = tree.qry(n);
			ll xxx = s1 - (s2 - s1);//计算答案
			if(xxx < 0)
				xxx = -xxx;
			cout << xxx << "\n";
		}
	}
	return 0;
}
posted @ 2025-05-17 23:04  gbrrain  阅读(47)  评论(0)    收藏  举报