补题若干
[https://atcoder.jp/contests/abc428/tasks/abc428_e](树的直径性质)
题意:给定一棵树,求出每个结点距离它最远且标号最大的结点
思路:距离每个结点最远的一定是一条直径的端点,而树的直径大小固定,端点不固定。找到两个端点标号最大的直径,每次比较距离即可
代码:实现找树的直径算法:先随便挑一个点dfs,求出和它距离最远的点,它一定是一条直径的端点。再以它为端点迭代求一遍即可
int n;
const int M=5e5+5;
vector<int>e[M];
int d[M];
int du[M],dv[M];
void dfs(int u,int fa){
d[u]=d[fa]+1;
for(int v:e[u]){
if(v==fa)continue;
dfs(v,u);
}
}
void solve(){
cin>>n;
rep(i,1,n-1){
int u,v;cin>>u>>v;
e[u].pb(v);e[v].pb(u);
}
memset(d,0,sizeof d);
dfs(1,0);
int u=1,v=1;
rep(i,1,n){
if(d[i]>=d[u]){
u=i;
}
}
memset(d,0,sizeof d);
dfs(u,0);
rep(i,1,n)du[i]=d[i];
rep(i,1,n){
if(d[i]>=d[v]){
v=i;
}
}
memset(d,0,sizeof d);
dfs(v,0);
rep(i,1,n)dv[i]=d[i];
rep(i,1,n){
if(du[i]>dv[i])cout<<u<<endl;
else if(dv[i]>du[i])cout<<v<<endl;
else cout<<max(u,v)<<endl;
}
}
(https://atcoder.jp/contests/abc387/tasks/abc387_c)[数位DP]
题意:给定[l,r],求区间内 最高位的大小大于其他位 的数字个数
思路:
数位DP:先求[1,\(x\)]范围内符合条件的数的个数
步骤
- 先将\(x\)的每一位放入\(num\)数组,得出\(x\)一共有多少位
- int dfs():分别记录当前枚举到哪一位,是否到顶,当前枚举到的最高位的数位大小
- \(base case\):枚举到最后一位时:如果不顶着上界,那么最后一位取值不超过mx即有mx种,否则取min(最后一位,mx)
- 记录dp数组,如果已经搜索到,那么直接返回
- 分为mx当前为0(即前导0)和不为0的情况,下一位的上界是下一位数字大小或者9
int l,r;
int n;
int dp[50][2][10];
int num[30];
// 325
// 299
// 100
// 090
// 3
// 523
//325
//52 0 1 2 3
int dfs(int len,int top,int mx){
if(len==1){
return top?min(mx,num[1]+1):mx;
}
if(dp[len][top][mx])return dp[len][top][mx];
if(!mx){
int ans=0;
int mm=(top?num[len]:9);
for(int i=0;i<=mm;i++){
mx=i;
ans+=dfs(len-1,(i==num[len]&&top),mx);
}
dp[len][top][0]=ans;
return ans;
}
int ans=0;
int mm=(top?num[len]:9);
for(int i=0;i<=min(mx-1,mm);i++){
ans+=dfs(len-1,(i==num[len])&&top,mx);
}
dp[len][top][mx]=ans;
return ans;
}
int work(int x){
memset(dp,0,sizeof dp);
n=0;
while(x){
num[++n]=x%10;x/=10;
}
int ans=dfs(n,1,0);
return ans;
}
void solve(){
cin>>l>>r;
cout<<work(r)-work(l-1);
}
[https://atcoder.jp/contests/abc390/tasks/abc390_d](搜索)
题意:给定一个数组,可以进行任意次操作,每次操作可以把某一个位置上的元素清空,并把元素加在另一个元素上
思路:
dfs
考虑操作本质:每次将一个元素放在一个新的位置,或者加到旧的位置上
int n;
int a[15];
unordered_map<int,int>mp;
int res=0;
int p[15];
void dfs(int cur,int x){
if(cur>n){
int ans=0;
rep(i,1,x)ans^=p[i];
if(!mp.count(ans)){
res++;
mp[ans]=1;
}
return ;
}
for(int i=1;i<=x;i++){
p[i]+=a[cur];
dfs(cur+1,x);
p[i]-=a[cur];
}
p[x+1]=a[cur];
dfs(cur+1,x+1);
}
void solve(){
cin>>n;
rep(i,1,n)cin>>a[i];
dfs(1,0);
cout<<res;
}
(https://atcoder.jp/contests/abc417/tasks/abc417_d)[二分+DP]
题意:
给定\(n\)个礼物的价值\(P_i\),设\(x\)为当前心情,若\(P_i > x\) ,则\(x\)+\(A_i\) ,否则\(x\)-\(B_i 给定\)q\(个\)x\(,求最后操作后的\)x$
思路:
可以通过反向dp求出从第\(i\)个礼物开始,当前心情为\(j\)时最后的答案\(dp[i][j]\)
发现当\(x\)超过\(1000\)时,下一步一定减小,因此二分求出当初始\(x\)比较大时,哪一步到了\(1000\)的循环圈
const int M=1e4+5;
int n,q;
int p[M],a[M],b[M];
int dp[M][1005];
int s[M];
void solve(){
cin>>n;
rep(i,1,n){
cin>>p[i]>>a[i]>>b[i];
}
for(int j=0;j<=1000;j++){
dp[n+1][j]=j;
}
for(int i=n;i>=1;i--){
for(int j=0;j<=1000;j++){
dp[i][j] = dp[i+1][(j>p[i])?max(0ll,j-b[i]):j+a[i]];
}
}
rep(i,1,n)s[i]=s[i-1]+b[i];
int q;cin>>q;
while(q--){
int x;cin>>x;
if(x-1000>s[n]){
cout<<x-s[n]<<endl;
}else if(x<=1000){
cout<<dp[1][x]<<endl;
}else{
int id = lower_bound(s+1,s+1+n,x-1000)-s;
cout<<dp[id+1][x-s[id]]<<endl;
}
}
}
(https://www.luogu.com.cn/problem/AT_abc382_e)[概率DP]
题意:
有无限个卡包,每个卡包有\(N\)张卡,每张卡为目标卡牌的概率固定是\(P_i\),求获得\(x\)张目标卡牌的期望开包次数
思路:
先求一包中获得目标卡牌的概率
记g[i]为获得\(i\)张卡牌的概率
枚举每一个卡牌都有: g[i] = g[i] x (1-p[j]) + g[i-1] x p[j] :即这张牌是否是目标牌
记f[i]为至少获得\(i\)张卡牌的开包期望
那么有f[i] += f[max(0,i-j)] x g[j]
最后f[i]除以(1-g[0])
const int M=5e3+5;
double p[M];
int ans;
double g[M];
double f[M];
void solve(){
int n,x;cin>>n>>x;
rep(i,1,n)cin>>p[i],p[i]/=100;
g[0]=1;
rep(i,1,n){
for(int j=n;j>=1;j--){
g[j]=g[j]*(1-p[i])+g[j-1]*p[i];
}
g[0]*=(1-p[i]);
}
for(int i=1;i<=x;i++){
f[i]=1;
for(int j=1;j<=n;j++){
f[i]+=f[max(0ll,i-j)]*g[j];
}
f[i]/=(1-g[0]);
}
cout<<fixed<<setprecision(16)<<f[x]<<endl;
}

浙公网安备 33010602011771号