2021年度训练联盟热身训练赛第三场

本文精选了算法竞赛中的典型题目并提供了解决方案,涵盖了概率DP、区间DP、Floyd算法、DP递推、线段树等核心算法,并通过具体题目进行详细解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://ac.nowcoder.com/acm/contest/13168

A 水题

B

发现奇数中间的块会被分隔,大块由好多小块组成。

#include <bits/stdc++.h>
using namespace std;
int main()
{
    long long a,b;
    cin>>a>>b;
    long long ans = __gcd(a,b);
    a /= ans;
    b /= ans;
    if(a & 1 && b & 1) cout<<ans;
    else cout<<0;
    return 0;
}

C水题

D水题

E

F

G概率DP

方程很简单,注意初始化就好
概率DP的0不是没有是,选了0个,是一种情况

#include <bits/stdc++.h>
using namespace std;
double dp[105][105];
double a[105];
bool cmp(double a,double b){
    return a > b;
}
int main()
{
    int n;scanf("%d",&n);
    for(int i = 1;i <= n;i ++) scanf("%lf",&a[i]),a[i] /= 100;
    sort(a + 1,a + 1 + n,cmp);
    dp[0][0] = 1;
    for(int i = 1;i <= n;i ++){
        dp[i][0] = dp[i - 1][0] * (1 - a[i]);
    }
    for(int i = 1;i <= n;i ++){
        for(int j = 1;j <= i;j ++){
            //printf("%f\n",a[i]);
            dp[i][j] = dp[i - 1][j - 1] * a[i];
            dp[i][j] += dp[i - 1][j] * (1.0 - a[i]);
        }
    }
    double ans,res;
    res = 0;
    for(int i = 1;i <= n;i ++){
    ans = 0;
    for(int j = 1;j <= i;j ++){
        //printf("%.10f",dp[i][j]);
        ans += dp[i][j] * pow((double)j,(double)j/(double)i);
    }
    res = max(res,ans);
    }
    printf("%.10f",res);
    return 0;
}

H区间DP

找到互不相交的区间
三重循环,最外层是len,然后是起点,和分隔
每个len都会有新的 a [ i ] [ i + l e n − 1 ] a[i][i + len - 1] a[i][i+len1]没有被更新过

#include <bits/stdc++.h>
using namespace std;
int dp[505][505],a[505][505];
int main()
{
    int n;scanf("%d",&n);
    for(int i = 1;i <= n;i ++)
        for(int j =1;j <= n;j ++) scanf("%d",&a[i][j]);
    for(int t = 1;t < n;t ++){
        for(int j = 1;j <= n - t;j ++){
            for(int k = j;k <= j + t;k ++){
                dp[j][j + k] = max(dp[j][j + k],dp[j + 1][k - 1] + dp[k][j + k - 1] + a[j][k]);
            }
        }
    }
    printf("%d",dp[1][n]);
    return 0;
}

I Floyd

两遍Floyd,不算难,题目的加油站已经疯狂提示Floyd
注意需要思考清楚在写,第一遍Floyd的目的是建立新图,在新图上跑第二遍Floyd

#include <bits/stdc++.h>
using namespace std;
const int Mn = 505;
#define ll long long
ll dis[Mn][Mn];
ll ans[Mn][Mn];
ll g[Mn][Mn];
int vis[Mn];
const ll inf = 0x3f3f3f3f3f3f3f3f;
int main()
{
    ll n,m,k,d;
    scanf("%lld%lld%lld%lld",&n,&m,&k,&d);
    int a;
    for(int i = 1;i <= k;i ++){
        scanf("%d",&a);
        vis[a] = 1;
    }
    memset(ans,0x3f,sizeof(ans));
    memset(dis,0x3f,sizeof(dis));
    int b;
    ll c;
    for(int i = 1;i <= m;i ++){
        scanf("%d%d%lld",&a,&b,&c);
        g[a][b] = g[b][a] = c;
        dis[a][b] = dis[b][a] = c;
    }
    for(int k = 1;k <= n;k ++){
        for(int i = 1;i <= n;i ++){
            for(int j = 1;j <= n;j ++){
                if(i == j) dis[i][j] = 0LL;
                else dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
             }
        }
    }
    //printf("%lld\n",dis[2][5]);
    if(dis[1][n] == inf){
        puts("stuck");
    }else
    if(dis[1][n] <= d){
        printf("%lld\n",dis[1][n]);
    }else{
        vis[1] = vis[n] = 1;

    for(int i = 1;i <= n;i ++){
        if(vis[i] == 0) continue;
        for(int j = 1;j <= n;j ++){
            if(vis[j] == 0) continue;

            if(dis[i][j] > d) ans[i][j] = inf;
            else ans[i][j] = dis[i][j];
        }
    }
    for(int k = 1;k <= n;k ++){
        if(vis[k] == 0) continue;
        for(int i = 1;i <= n;i ++){
            if(vis[i] == 0) continue;
            for(int j = 1;j <= n;j ++){
                if(vis[j] == 0) continue;
                if(i == j) ans[i][j] = 0LL;
                else{
                    ans[i][j] = min(ans[i][j],ans[i][k] + ans[k][j]);
                    //printf("%lld %lld %lld %d %d %d\n",ans[i][j],ans[i][k],ans[k][j],i,j,k);
                }
            }
        }
    }
    //printf("%lld\n",ans[2][5]);
    if(ans[1][n] == inf){
        puts("stuck");
    }else printf("%lld",ans[1][n]);
    }
    return 0;
}

K 简单DP(实际上是递推)

外国就是喜欢出DP

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int Mn = 1e5 + 5;
ll dp[Mn][30];
int vis[30];
int lst[300];
char s[Mn];
int main()
{
    scanf("%s",s + 1);
    int n = strlen(s + 1);
    ll ans = 0;
    for(int i = 1;i <= n;i ++){
        int t = s[i] - 'a' + 1;
        dp[i][t] = dp[i - 1][t] + 1;
        lst[t] = i;
        for(int j = 1;j <= 26;j ++){
            if(j == t) continue;
            if(dp[i][t] - dp[lst[j]][t] == 1 && lst[j])
            ans ++;
            dp[i][j] = dp[i - 1][j];
        }

    }
    cout<<ans;
    return 0;
}

L 线段树

是过得比较麻烦的一道题了
需要注意的地方是
1、题目中的ans不是由单独区间就可以跟新出来的,所以返回值最好是整个结构体,方便代码短一些
2、从右往左和从左往右的关系,就是把真个数组倒过来就行了,倒过来之后ans同样适用
3、日期取模问题,先-1后+1
4、左右,右左问题,左右是从0开始,所以右左是从 n + 1 n + 1 n+1开始

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int Mn = 1e5 + 5;
struct node{
    ll mx,mi,ans;
}tree[20][Mn << 2];
ll a[20][Mn],d[Mn];
ll f[20] = {0,0,1,2,3,2,1,0};
#define mid ((l + r) >> 1)
#define lson now,p<<1,l,mid
#define rson now,p<<1|1,mid+1,r
inline void updata(int now,int p){
    tree[now][p].mx = max(tree[now][p << 1].mx,tree[now][p<<1|1].mx);
    tree[now][p].mi = min(tree[now][p << 1].mi,tree[now][p<<1|1].mi);
    tree[now][p].ans = max(tree[now][p<<1].ans,max(tree[now][p<<1|1].ans,tree[now][p<<1|1].mx - tree[now][p << 1].mi));
}
void build(int now,int p,int l,int r){
    if(l == r){
        tree[now][p].mi = tree[now][p].mx = a[now][l];
        tree[now][p].ans = 0;
        return ;
    }
    build(lson);
    build(rson);
    updata(now,p);
    //printf("%d %d %d %d %d\n",l,r,tree[now][p].ans,tree[now][p].mx,tree[now][p].mi);
}
node Query(int now,int p,int l,int r,int ql,int qr){
    if(ql <= l && r <= qr){
        return tree[now][p];
    }
    node ans,al,ar;
    if(qr <= mid) return Query(lson,ql,qr);
    if(ql > mid) return Query(rson,ql,qr);
    al = Query(lson,ql,mid);
    ar = Query(rson,mid + 1,qr);
    ans.mi = min(al.mi,ar.mi);
    ans.mx = max(al.mx,ar.mx);
    ans.ans = max(al.ans,max(ar.ans,ar.mx - al.mi));
    return ans;
}
int main(){
    int n;scanf("%d",&n);
    for(int i = 1;i <= n;i ++){
        scanf("%lld%lld",&a[0][i],&d[i]);
    }
    int now = 0;
    for(int i = 1;i <= 7;i ++){
        now = i;
        for(int j = 1;j <= n;j ++){
            a[i][j] = a[0][j] + f[now] * d[j];
            now ++;
            now = (now - 1) % 7 + 1;
        }
    }
    for(int i = 1;i <= 7;i ++) build(i,1,1,n);
    for(int i = 1;i <= n / 2;i ++){
        swap(a[0][i],a[0][n + 1 - i]);
        swap(d[i],d[n + 1 - i]);
    }
    for(int i = 8;i <= 14;i ++){
        now = i - 7;
        for(int j = 1;j <= n;j ++){
            a[i][j] = a[0][j] + f[now] * d[j];
            now ++;
            now = (now - 1) % 7 + 1;
        }
    }
    for(int i = 8;i <= 14;i ++) build(i,1,1,n);
    int q;scanf("%d",&q);
    while(q--){
        int l,r;scanf("%d%d",&l,&r);
        int flag = 1;
        if(l > r){
            flag = 0;
            l = n + 1 - l;
            r = n + 1 - r;
        }
        int x = (l - 1) % 7 + 1;
        int d = x - 1;
        int nw = 1 - d;
        nw += 7;
        if(nw == 8) nw = 1;
        if(flag == 0) nw += 7;
        for(int i = 1;i <= n;i ++){
            //printf("*%d ",a[nw][i]);
        }
        //puts("");
        printf("%lld\n",Query(nw,1,1,n,l,r).ans);
    }
    return 0;
}

M 三分或者结论

相似三角形或者三分
可以当做三分练手题

#include <bits/stdc++.h>
using namespace std;
const double esp = 1e-7;
double w,g,h,r;
inline double s(double x){
    double ans = sqrt((g - r) * (g - r) + x * x);
    ans += sqrt((h - r) * (h - r) + (w - x) * (w - x));
    return ans;
}
inline void Slove(){

    scanf("%lf%lf%lf%lf",&w,&g,&h,&r);
    double ans;
    if(g > h) swap(g,h);
    ans = sqrt(w * w + (h - g) * (h - g));
    printf("%.10f ",ans);
    ans = 1e30;
    double l = 0,r = w;
    double mid1,mid2;
    while(r - l >= esp){
        mid1 = l + (r - l) / 3.0;
        mid2 = l + (r - l) / 3.0 * 2;
       // printf("%.10f %.10f %.10f %.10f \n",l,r,mid1,mid2);
       //printf("%.10f\n",ans);
        double a = s(mid1),b = s(mid2);
        if(a <= b ){
            ans = min(ans,a);
            ans = min(ans,b);
            r = mid2 - esp;
        }else{
            l = mid1 + esp;
            ans = min(ans,b);
            ans = min(ans,a);
        }
    }
    printf("%.10f\n",ans);
}
int main()
{
    int _;scanf("%d",&_);
    while(_--) Slove();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值