SMU Winter 2024 div2 ptlks的周报Week 5(3.4-3.10)

维护子树的全部子树的权值和时,需要用到树的DFS序列,树的每个子树都对应DFS序列中的连续一段

黄金树影

题意:给定一棵树及每个节点的权值,给定一组操作,输入 1 a x ,表示节点a权值加上x;输入 2 a ,表示询问节点a的子树权值和(包含a)。

考虑到树的DFS序列,则问题转变为对某个序列维护区间和以及单点修改,这里通过树状数组来维护。

代码

#include <bits/stdc++.h>
#define int long long
#define MOD 998244353
using namespace std;

int a[500005]={0};
int t[500005]={0};
int p[500005]={0};
int b[500005]={0};
int c[500005]={0};
int tms=1,n;
vector<int>g[500005];
vector<int>vis(500005,0);

int lowbit(int x){
	return x&-x;
}

int getsum(int x) {
	int ans = 0;
	while (x > 0) {
		ans = ans + c[x];
		x = x - lowbit(x);
	}
	return ans;
}

void add(int x, int k) {
	while (x <= n) {
		c[x] = c[x] + k;
		x = x + lowbit(x);
	}
}

void dfs(int x){
	vis[x]=1;
	t[tms]=x;
	p[x]=tms;
	tms++;
	for(int i=0;i<g[x].size();i++){
		if(!vis[g[x][i]]){
			dfs(g[x][i]);
			vis[g[x][i]]=0;
		}
	}
	b[x]=tms-1;
}

int32_t main() {
	int T = 1;
	//cin >> T;
	while (T--) {
		int m;
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=2;i<=n;i++){
			int u;
			cin>>u;
			g[i].emplace_back(u);
			g[u].emplace_back(i);
		}
		dfs(1);
		for(int i=1;i<=n;i++){
			add(i,a[t[i]]);
		}
		for(int i=0;i<m;i++){
			int x,y;
			cin>>x>>y;
			if(x==1){
				int k;
				cin>>k;
				add(p[y],k);
			}else{
				cout<<getsum(b[y])-getsum(p[y]-1)<<endl;
			}
		}
	} 
}

当题目中出现n过大的情况且涉及位运算时,可以考虑逐位处理。

xor^2

题意:求\(\displaystyle\sum_{i=1}^n\displaystyle\sum_{j=1}^n(i xor j)\)\((1\leq n\leq 10^{18})\)

逐位考虑,若第i位出现1的个数为\(a[i]\),则第i位对答案的贡献为\(a[i]*(n-a[i])*2*2^i\),注意不要超过数据范围即可。

#include <bits/stdc++.h>
#define int long long
#define MOD 998244353
using namespace std;

int32_t main() {
	int T = 1;
	cin >> T;
	while (T--) {
		int a[10086];
		int n,s=0;
		cin >> n;
		int x=0,p=n;
		while(p){
			a[x]=p%2;
			x++;
			p>>=1;
		}
		for(int i=0;i<x;i++){
			a[i]=n/(1ll<<(i+1))*((1ll<<(i)));
			if(n%(1ll<<(i+1))>=(1ll<<(i))){
				a[i]+=n%(1ll<<(i))+1;
			}
			s+=a[i]%MOD*(((n-a[i]))%MOD)%MOD*2%MOD*((1ll<<(i))%MOD)%MOD;
			s%=MOD;
		}
		cout<<s<<endl;
	} 
}

对于质因数分解相关的问题,可以只枚举\(\sqrt{n}\)范围内的素数来减小时间复杂度,也可通过筛法来求素数再次减小时间复杂度

求除数

题意:给定n,求n的因数的个数\((1\leq n\leq 10^8)\)

有多组数据,所以先计算\((1, 10^4)\)范围内素数,可通过欧拉筛来求,再对每个n枚举素数分解质因数,最后输出即可。

#include <bits/stdc++.h>
#define int long long
#define MOD 1000000007
using namespace std;

int pos=0;
vector<int> q(10005),p(10005,1);

void prime(int x){
	p[0]=0,p[1]=0;
	for(int i=2;i<=x;i++){
		if(p[i]){
			q[pos]=i;
			pos++;
		}
		for(int j=0;j<pos;j++){
			if(i*q[j]>x)break;
			p[i*q[j]]=0;
			if(i%q[j]==0)break;
		}
	}
}

int32_t main() {
	int T = 1;
	cin >> T;
	prime(1e4);
	while (T--) {
		int n,s=1;
		cin>>n;
		for(int i=0;i<pos;i++){
			int x=0;
			while(n%q[i]==0){
				n/=q[i];
				x++;
			}
			s*=(x+1);
			if(n==1)break;
		}
		if(n>1){
			s*=2;
		}
		cout<<s<<endl;
	}
}
posted @ 2024-03-10 15:11  ptlks  阅读(12)  评论(0编辑  收藏  举报