2022“杭电杯”中国大学生算法设计超级联赛(4)D、F、G、K

这篇博客主要探讨了两个算法问题的解决方案。第一个问题是关于费用累加的动态规划策略,要求在不超过特定限制的情况下最大化总和,代码中展示了如何处理这个问题。第二个问题涉及线性基,解释了如何找到一个数组中元素异或和的最大值。文章通过AC代码展示了具体的实现细节,并讨论了时间复杂度和效率。
摘要由CSDN通过智能技术生成

Link with Equilateral Triangle

d

AC代码:

d

BIT Subway

阅读理解题,注意有可能从小于100直接过渡到200+,这个题答案验证是字符串类型,注意只能保留三位小数

AC代码:

#include <bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<n;i++)
using namespace std;
using LL = long long;

void Solve() {
    int n;
    cin >> n;
    vector<double> a(n);
    for (int i = 0; i < n; i++) {
        scanf("%lf", &a[i]);
    }
    double ans = 0.0;
    double sum = 0.0;
    bool ok = false;
    bool ok1 = false;
    for (int i = 0; i < n; i++) {
        if (sum >= 100 && sum < 200) {
            sum += a[i] * 0.8;
        }
        else if (sum >= 200) {
            sum += a[i] * 0.5;
        }
        else {
            sum += a[i];
        }
        if (!ok) {
            if (ans + a[i] >= 100 && (a[i] - (100 - ans)) * 0.8 + 100.0 >= 200) {
                double c = 100 - ans;
                ans = 100.0;
                a[i] = 1.0 * a[i] - c;
                double d = (200.0 - ans);
                double l = 0.0, r = a[i];
                for (int i = 0; i < 50; i++) {
                    double mid = (l + r) / 2;
                    if (mid * 0.8 > d) {
                        r = mid;
                    }
                    else {
                        l = mid;
                    }
                }
                ans = 200.0;
                ans += 1.0 * (a[i] * 1.0 - l) * 0.5;
                ok = true;
                ok1 = true;
                continue;
            }
            if (ans + a[i] >= 100) {
                double c = ans + a[i] - 100;
                ans = 100.0;
                ans += 1.0 * c * 0.8;
                ok = true;
            }
            else {
                ans += a[i];
            }
        }
        else {
            if (ans + 1.0 * a[i] * 0.8 >= 200.0 && !ok1) {
                double d = (200.0 - ans);
                double l = 0.0, r = 1.0 * a[i];
                for (int i = 0; i < 50; i++) {
                    double mid = (l + r) / 2;
                    if (mid * 0.8 > d) {
                        r = mid;
                    }
                    else {
                        l = mid;
                    }
                }
                ans = 200.0;
                ans += 1.0 * (a[i] * 1.0 - l) * 0.5;
                ok1 = true;
            }
            else if (ok1 && ans >= 200) {
                ans += 0.5 * a[i];
            }
            else {
                ans += 0.8 * a[i];
            }
        }
    }
    printf("%.3lf %.3lf\n", ans, sum);
}

int main() {

    int T;
    scanf("%d", &T);
    rep (i, 0, T) {
        Solve();
    }

    return 0;
}

Climb Stairs

因为只能向上跳k步向下跳1步,走过的楼层不能再走,而且要杀死所有怪兽,只能一段一段考虑,否则下面一定有漏掉的怪兽没有打,所以每次攻击力不够了,我们应从能跳到的最远处(或最后一个怪兽的位置,否则RE)且能够打死的怪兽往后枚举,这样保证都能杀死的同时还可以增加攻击力,如果又遇上打不过的,就将攻击力初始化再重复上述操作,直到遇上最开始打不过的怪兽,如果还打不过,那就真打不过了,否则跳到开始增加攻击力的楼层的下一个位置,这个位置要么是跳不到的最近的地方,要么是最初攻击力打不过的地方

这样的暴力竟然比他的标程线段树还快,时间复杂度确实没什么问题

 

AC代码:

#include <bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<n;i++)
using namespace std;
using LL = long long;
const int mod = 1e9 + 7;

void Solve() {
    LL n, fi, k;
    cin >> n >> fi >> k;
    vector<LL> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        if (a[i] > fi) {
            int j = i + k - 1;
            if (i + k - 1 > n) {
                j = n;
            }
            int l = j;
            LL start = fi;
            bool ok = false;
            for (int z = j; z >= i; z--) {
                if (fi >= a[z]) {
                    fi += a[z];
                    if (z == i) {
                        ok = true;
                        break;
                    }
                }
                else {
                    fi = start;
                    l = z - 1;
                }
            }
            if (ok) {
                i = l + 1;
            }
            else {
                cout << "NO\n";
                return;
            }
        }
        else {
            fi += a[i];
        }
    }
    cout << "YES\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    rep (i, 0, T) {
        Solve();
    }

    return 0;
}

标程:

#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define Fill(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int inf=2139062143;
const int MOD=998244353;
const int MAXN=200100;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline ll readll()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace CALC
{
    inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:a+b;}
    inline int mns(int a,int b){return a-b<0?a-b+MOD:a-b;}
    inline int mul(int a,int b){return (1LL*a*b)%MOD;}
    inline void inc(int &a,int b){a=pls(a,b);}
    inline void dec(int &a,int b){a=mns(a,b);}
    inline void tms(int &a,int b){a=mul(a,b);}
    inline int qp(int x,int t,int res=1)
        {for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
    inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,a[MAXN],k,las,cur,l,r;
pair<ll,int> q[MAXN];
ll sum[MAXN],f[MAXN],mxf[MAXN];
ll mn[MAXN<<2];
void build(int k,int l,int r)
{
    if(l==r) {mn[k]=f[l];return ;}int mid=l+r>>1;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    mn[k]=min(mn[k<<1],mn[k<<1|1]);
}
int res=inf;
void query(int k,int l,int r,int a,int b,int w)
{
    if(a<=l&&r<=b)
    {
        if(mn[k]>w||res!=inf) return ;
        if(l==r) {res=l;return ;}int mid=l+r>>1;
        if(mn[k<<1]<=w) query(k<<1,l,mid,a,b,w);
        else if(mn[k<<1|1]<=w) query(k<<1|1,mid+1,r,a,b,w);
        return ;
    }
    int mid=l+r>>1;
    if(a<=mid) query(k<<1,l,mid,a,b,w);
    if(b>mid) query(k<<1|1,mid+1,r,a,b,w);
}
int solve()
{
    n=read(),a[0]=sum[0]=read(),k=min(read(),n);
    rep(i,1,n) a[i]=read(),sum[i]=sum[i-1]+a[i];
    f[0]=sum[0]<<1;rep(i,1,n) f[i]=max(sum[i]+a[i],f[i-1]);
    rep(i,0,n) f[i]=f[i]-sum[i];
    //rep(i,0,n) cout<<f[i]<<" ";puts("");
    build(1,1,n);
    las=-1,cur=0;
    while(cur!=n)
    {
        res=inf;
        query(1,1,n,cur+1,min(las+k+1,n),sum[cur]);
        //cout<<las<<" "<<cur<<" "<<res<<endl;
        if(res==inf) return 0;
        las=cur,cur=res;
    }
    return 1;
}
int main()
{
    rep(T,1,read()) puts(solve()?"YES":"NO");
}

Link is as bear

线性基板板题,从这 个数里任取一些数异或起来的方案,都是可以构造出对应的操作来 做到的,问题完全等价于给n个数,从中选一些数,使得这些数的异或和最大。

AC代码:

#include <bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<n;i++)
using namespace std;
using LL = long long;
const int mod = 1e9 + 7;


void Solve() {
    int n;
    cin >> n;
    vector<LL> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    int k = 0;
    for (int i = 62; i >= 0; i--) {
        for (int j = k; j < n; j++) {
            if (a[j] >> i & 1) {
                swap(a[j], a[k]);
                break;
            }
        }
        if (!(a[k] >> i & 1)) {
            continue;
        }
        for (int j = 0; j < n; j++) {
            if (j != k && (a[j] >> i & 1)) {
                a[j] ^= a[k];
            }
        }
        k++;
        if (k == n) {
            break;
        }
    }
    LL ans = 0;
    for (int i = 0; i < k; i++) {
        ans ^= a[i];
    }
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    rep (i, 0, T) {
        Solve();
    }

    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值