20南京站M题(树上dp)19南京站J题(KM算法)+思维
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20南京站M题(树上dp)19南京站J题(KM算法)+思维相关的知识,希望对你有一定的参考价值。
M. Monster Hunter
树上背包。对于父节点和子节点,都是可选可不选,删除的点是任意的,但要满足价值最小。
状态设计:
dp[u][num][0]
表示以u为根节点(不包含自身),选择num个子节点,能获取的最小价值。
dp[u][num][1]
表示以u为根节点(包含自身),选择num个子节点,能获取的最小价值。
状态转移:f[u][i+j][0]=min(f[u][i+j][0],f[u][i][0]+min(f[v][j][0],f[v][j][1]));
u结点不选被删掉,因此子节点并不对父节点产生额外的的贡献。
相反,f[u][i+j][1]=min(f[u][i+j][1],f[u][i][1]+min(f[v][j][0],f[v][j][1]+hp[v]));
u结点被选择,子节点的权值会被父节点累加上。
实现:双重循环枚举出u和v不同的取值,从后向前,滚动更新。
#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
#define down 0.996
using namespace std;
const double eps=1e-8;
const double inf=1e18;
const int mod=998244353;
const int P=131;
const int N=3e3+5;
int n,a[N],hp[N],f[2005][2005][2],sz[N];
vector<int>e[N];
void dfs(int u,int fa)
f[u][1][1]=hp[u],f[u][0][0]=0;
sz[u]=1;
for(int v:e[u])
if(v==fa) continue;
dfs(v,u);
for(int i=sz[u];i>=0;i--)
for(int j=sz[v];j>=0;j--)
f[u][i+j][0]=min(f[u][i+j][0],f[u][i][0]+min(f[v][j][0],f[v][j][1]));
f[u][i+j][1]=min(f[u][i+j][1],f[u][i][1]+min(f[v][j][0],f[v][j][1]+hp[v]));
sz[u]+=sz[v];
void solve()
cin>>n;
for(int i=0;i<=n;i++) e[i].clear(),sz[i]=0;
for(int i=2;i<=n;i++)
int x;cin>>x;
e[i].push_back(x);e[x].push_back(i);
for(int i=1;i<=n;i++) cin>>hp[i];
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
f[i][j][0]=f[i][j][1]=inf;
dfs(1,0);
for(int i=0;i<=n;i++)
cout<<min(f[1][n-i][0],f[1][n-i][1])<<" ";
cout<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
模板 - KM算法(O(n^3))(二分图最大权完美匹配)
(记录下大佬的链接~)
J. Spy
具体证明过程:https://zhuanlan.zhihu.com/p/562593632
思考过程:
读懂题意后,对于Bob来说需要对两类成员进行随机组合,使得累加值在和Alice进行比赛时,能得到最大的分数,要求出最大的期望分数*n的值。
1.对于数组B和C组合出的数组D,有n!种方案,那么期望的分数即为p[i]/n! (if d[i]>a[i])
2.对于数组d的搭配也有n!种方案,则平均每个d[i]有n!/n
种方案数。
3.因此公式为: (n!/n)*(p[i]的和/n!)
KM板子是抄其他大佬的
#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
#define down 0.996
using namespace std;
const double eps=1e-8;
const double inf=1e18;
const int mod=998244353;
//const int P=131;
const int N=400+5;
int w[N][N],lx[N],ly[N];
bool visx[N],visy[N];
int match[N],delta,n,c[N],p[N];
int A[N],P[N],B[N],C[N];
void bfs(int x) // O(n^3)的KM板子
int a,y=0,y1=0;
for(int i=1;i<=n;i++) p[i]=0,c[i]=inf;
match[y]=x;
do
a=match[y],delta=inf,visy[y]=1;
for(int b=1;b<=n;b++)
if(!visy[b])
if(c[b]>lx[a]+ly[b]-w[a][b])
c[b]=lx[a]+ly[b]-w[a][b],p[b]=y;
if(c[b]<delta) delta=c[b],y1=b;
for(int b=0;b<=n;b++)
if(visy[b]) lx[match[b]]-=delta,ly[b]+=delta;
else c[b]-=delta;
y=y1;
while(match[y]);
while(y)
match[y]=match[p[y]],y=p[y];
int KM()
for(int i=1;i<=n;i++) match[i]=lx[i]=ly[i]=0;
for(int i=1;i<=n;i++)
for(int i=0;i<=n;i++) visy[i]=0;
bfs(i);
int res=0;
for(int i=1;i<=n;i++) res+=w[match[i]][i];
return res;
void solve()
cin>>n;
for(int i=1;i<=n;i++) cin>>A[i];
for(int i=1;i<=n;i++) cin>>P[i];
for(int i=1;i<=n;i++) cin>>B[i];
for(int i=1;i<=n;i++) cin>>C[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
int sum=0;
for(int k=1;k<=n;k++)
if(A[k]<B[i]+C[j]) sum+=P[k];
w[i][j]=sum;
cout<<KM()<<endl;
signed main()
ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
C.Rolling Girl
数论题。
我想到了很多东西,都是对的。没做出来一是复杂度没算对,不敢尝试;而是代码的实现没找到适合的写法,导致没自信。
思路:
1.很容易想到对于不同步长走过的数字要选取一个权值最小的点。
2.对于和n互为质数的步长,要走n轮。
3.二则相乘为该步长的跳跃次数。写法可直接对1~n进行遍历,测试下来最大不到4e8,可以过题。
太妙了。
#include <bits/stdc++.h>
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
#define down 0.996
using namespace std;
const int N=1e7+5;
const int inf=1e18;
const int p=1004535809;
unsigned seed, mod;
unsigned read()
seed ^= seed << 13;
seed ^= seed >> 5;
seed ^= seed << 7;
return seed % mod + 1;
int n,a[N],ans[N];
int fun(int x)
int mi=inf;
for(int i=x;i<=n;i+=x)
if(mi>a[i]) mi=a[i];
return mi%p*(n/x)%p;
void solve()
cin>>n;
cin>>seed>>mod;
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++)
if(n%i==0)
ans[i]=fun(i);
for(int j=i;j<=n;j+=i) ans[j]=ans[i];
int sum=0;
for(int i=1;i<=n;i++) sum=(sum+ans[i])%p;
cout<<sum<<endl;
signed main()
ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
D. Masha and a Beautiful Tree
递推做法:
#include <bits/stdc++.h>
//#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\\n'
#define ULL unsigned long long
#define down 0.996
using namespace std;
const double eps=1e-8;
const double inf=1e18;
const int mod=998244353;
//const int P=131;
const int N=3e6+5;
int qpow(int x,int y)
int res=1;
while(y)
if(y&1) res=res*x;
x=x*x;
y>>=1;
return res;
int n,a[N];
void solve()
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=0,flag=0;
for(int i=0;i<=n;i++)
int g=qpow(2,i);
int p=1,q=1+g;
if(q>n) break;
while(q<=n)
if(a[p]+g==a[q])
//cout<<1<<" "<<p<<" "<<q<<endl;
p+=2*g;q+=2*g;
else if(a[p]==a[q2018ACM-ICPC亚洲区域赛南京站I题Magic Potion(网络流)
计蒜客 30994 - AC Challenge - [状压DP][2018ICPC南京网络预赛E题]
2019 ICPC南京站 B题 Chessboard组合数,乘法逆元