SMU Winter 2024 div2 ptlks的周报Week 1(1.22-1.28)
这周学习到的知识点有 树形dp(P8625,P1352),费马小定理(求\(a \cdot b^{-1}(mod_p)\)),背包dp(P1048,P8742),树状数组(P6225),位运算(P6225,U360489),\(Dijkstra\)算法(ABC325E),拓扑排序(P1685)
P8625 [蓝桥杯 2015 省 B] 生命之树
思路
题目等价于选出一棵树,使它的总价值最大。不妨先以某个节点\(x\)为根,找出以\(x\)为根价值最大的树,有\(mx[x]=\sum mx[y_i]\),\(y_i\)为\(x\)的子节点。再考虑换根对\(mx\)的影响,不难得出,若更换后的根节点\(y\)与原根节点\(x\)相连,则换根只影响\(mx[x]\)与\(mx[y]\)的值,则有dp方程\(\begin{cases} mx[x]-=max\{mx[y],0\}\\ mx[y]+=max\{mx[x],0\}\\ \end{cases} \)
先dfs一遍确定初状态,再dfs进行换根dp。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a[100005];
int mxx=0;
vector<int> g[100005];
vector<int> mx(100005, -1e7);
vector<int> vis(100005, 0);
vector<int> vis1(100005, 0);
priority_queue<int> q;
void dfs(int x) {
vis[x] = 1;
for (int i = 0; i < g[x].size(); i++) {
if (!vis[g[x][i]]) {
dfs(g[x][i]);
if(mx[g[x][i]]>0)mx[x]+=mx[g[x][i]];
vis[g[x][i]] = 0;
}
}
}
void dfs1(int x) {
vis1[x]=1;
for (int i = 0; i < g[x].size(); i++) {
if(vis1[g[x][i]])continue;
if(mx[g[x][i]]>0){
int t=mx[g[x][i]];
mx[x]-=t;
if(mx[x]>0){
int tt=mx[x];
mx[g[x][i]]+=tt;
mxx=max(mxx,mx[g[x][i]]);
dfs1(g[x][i]);
mx[g[x][i]]-=tt;
}
mx[x]+=t;
}
vis1[g[x][i]]=0;
}
}
int32_t main() {
int T;
//cin>>T;
T = 1;
while (T--) {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
mx[i]=a[i];
}
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
dfs(0);
for(int i=0;i<n;i++){
mxx=max(mx[i],mxx);
}
dfs1(0);
cout<<mxx;
}
return 0;
}
P8742 [蓝桥杯 2021 省 AB] 砝码称重
思路
背包问题的变种,求的不是最佳方案而是总方案数。直接计数即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
unordered_map<int, vector<int> > hm;
int a[100005], n,f[105][100005];
int32_t main() {
int T;
//cin>>T;
T = 1;
while (T--) {
cin >> n;
int s = 0, sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum += a[i];
}
for(int i=1;i<=n;i++){
for(int j=sum;j;j--){
if(j==a[i])f[i][j]=1;
else if(f[i-1][j])f[i][j]=1;
else if(f[i-1][j+a[i]])f[i][j]=1;
else if(f[i-1][abs(j-a[i])])f[i][j]=1;
}
}
for(int i=1;i<=sum;i++){
if(f[n][i])s++;
}
cout<<s;
}
return 0;
}
P6225 [eJOI2019] 异或橙子
思路
想要得到某个区间内所有子区间的异或和的异或和。由异或性质不难得出,区间的所有子区间的异或和的异或和,对于重复偶数次的元素,我们可以直接消去。再由所有子区间的异或和可得,长度为偶数的区间的所有子区间的异或和的异或和为0,长度为奇数的区间的所有子区间的异或和的异或和为奇数项元素的异或和,即隔项异或和。我们只需维护数组的隔项异或和,则对数组分奇偶项建树状数组维护异或和即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int a[500005],b[500005],c[500005]={0},d[500005]={0};
unordered_map<int,int> hm;
int lowbit(int x){
return x&(-x);
}
int getsum(int *t,int l,int r){
int ll=0,rr=0;
while(l){
ll^=t[l];
l-=lowbit(l);
}
while(r){
rr^=t[r];
r-=lowbit(r);
}
return (ll^rr);
}
void add(int *t,int x,int k,int n){
while(x<=n){
t[x] = (t[x] ^k);
x = x + lowbit(x);
}
}
int32_t main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
//cin>>T;
T = 1;
while (T--) {
int n,m;
cin>>n>>m;
int n1=n-n/2,n2=n/2;
for(int i=1;i<=n;i++){
if(i%2){
cin>>a[(i+1)/2];
add(c,(i+1)/2,a[(i+1)/2],n1);
}else{
cin>>b[(i/2)];
add(d,(i)/2,b[(i)/2],n2);
}
}
while(m--){
int x;
cin>>x;
if(x==1){
int i,j;
cin>>i>>j;
if(i%2){
add(c,(i+1)/2,(a[(i+1)/2]^j),n1);
a[(i+1)/2]=j;
}else{
add(d,(i)/2,(b[(i)/2]^j),n2);
b[(i)/2]=j;
}
}else{
int i,j;
cin>>i>>j;
if((j-i)%2){
cout<<0<<endl;
}else{
if(i%2){
cout<<getsum(c,(i+1)/2-1,(j+1)/2)<<endl;
}else{
cout<<getsum(d,(i)/2-1,(j)/2)<<endl;
}
}
}
}
}
return 0;
}
J.P1685 游览
思路
由于后继节点的更新要在前驱节点之后,故采用拓扑排序,每次更新入度为0的点,dp方程为\(\begin{cases} cnt[v]+=cnt[u]\\ s[v]+=s[u]+cnt[u]*w\\ \end{cases}\)
代码
#include <bits/stdc++.h>
#define int long long
#define MAX (int)1e18
using namespace std;
unordered_map<int, vector<int> > hm;
int n,x,y,t,sum=0;
vector<pair<int,int>> g[10005];
vector<int> cnt(10005, 0),ru(10005, 0);
vector<int> mn(10005, 0);
vector<int > q;
void f(int s){
for(int i=0;i<g[s].size();i++){
cnt[g[s][i].first]=(cnt[g[s][i].first]+cnt[s])%10000;
mn[g[s][i].first]=(mn[g[s][i].first]+mn[s]+cnt[s]*g[s][i].second)%10000;
ru[g[s][i].first]--;
if(!ru[g[s][i].first])f(g[s][i].first);
}
}
int32_t main() {
int T;
//cin>>T;
T = 1;
while (T--) {
int m,s;
cin >> n >> m>>x>>y>>t;
for (int i = 0; i < m; i++) {
int u,v,w;
cin>>u>>v>>w;
u--;v--;
g[u].emplace_back(make_pair(v,w));
ru[v]++;
}
cnt[x-1]=1;
f(x-1);
cout<<(mn[y-1]+t*(cnt[y-1]-1))%10000;
}
return 0;
}