SMU Winter 2024 div2 ptlks的週報Week 5(3.4-3.10)

ptlks發表於2024-03-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;
	}
}

相關文章