补题若干

[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\)]范围内符合条件的数的个数

步骤

  1. 先将\(x\)的每一位放入\(num\)数组,得出\(x\)一共有多少位
  2. int dfs():分别记录当前枚举到哪一位,是否到顶,当前枚举到的最高位的数位大小
  3. \(base case\):枚举到最后一位时:如果不顶着上界,那么最后一位取值不超过mx即有mx种,否则取min(最后一位,mx)
  4. 记录dp数组,如果已经搜索到,那么直接返回
  5. 分为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;
}
posted @ 2025-11-06 21:39  Marinaco  阅读(7)  评论(0)    收藏  举报
//雪花飘落效果