牛客网暑期ACM多校训练营(第五场)

本文总结了多项编程竞赛中常见的算法技巧,包括二分查找、动态规划、最大匹配等,并提供了具体的实现代码示例。

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

怎么大家都这么强啊。。。完全打不过啊。。。
A
二分答案check一下。

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
const double eps=1e-6;
int n,k,s[N],c[N];
double tmp[N];
double ans;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=0; i<n; i++)
        scanf("%d",&s[i]);
    for(int i=0; i<n; i++)
        scanf("%d",&c[i]);
    double l=0,r=1000;
    while(l<r-eps){
        double mid=(l+r)/2;
        for(int i=0; i<n; i++){
            tmp[i]=1.0*s[i]*(c[i]-mid);
        }
        sort(tmp,tmp+n);
        double ans=0;
        for(int i=n-1; i>=k; i--){
            ans+=tmp[i];
        }
        if(ans>=0) l=mid;
        else r=mid;
    }
    printf("%lf\n",l);
    return 0;
}

B
膜一下配个方就是pell方程了,这个造题的姿势感觉不是很优美。

m=input()
ans=0
if m<=2:
    ans=2
elif m<=6:
    ans=6
elif m<=12:
    ans=12
elif m<=70:
    ans=70
elif m<=84:
    ans=84
elif m<=408:
    ans=408
if(ans==0):
    a=6
    b=84
    c=0
    while 1:
        c=b*14-a
        if c>=m:
            ans=c
            break
        a=b
        b=c
    a=12
    b=408
    c=0
    while 1:
        c=b*34-a
        if c>=m:
            if ans>c:
                ans=c
            break
        a=b
        b=c
    a=2
    b=70
    c=0
    while 1:
        c=b*34-a
        if c>=m:
            if ans>c:
                ans=c
            break
        a=b
        b=c
print ans

D
好像把奇数插到偶数里的最优位置是单调的,所以就一个个插就完了。比赛的时候想的是把偶数插到奇数里,可能要维护个单调不增序列什么的,应该也能过吧,没试。

#include <bits/stdc++.h>
using namespace std;
const int maxn=200003;
int n;
int a[maxn];
int rk[maxn];
int mi[maxn<<2];
int lz[maxn<<2];
void pushup(int u){
    mi[u]=min(mi[2*u],mi[2*u+1]);
}
void pushdown(int u){
    if(lz[u]==0)return;
    lz[2*u]+=lz[u];
    lz[2*u+1]+=lz[u];
    mi[2*u]+=lz[u];
    mi[2*u+1]+=lz[u];
    lz[u]=0;
}
void build(int u,int l,int r){
    lz[u]=0;
    if(l==r){
        mi[u]=0;
        return ;
    }
    int mid=(l+r)/2;
    build(2*u,l,mid);
    build(2*u+1,mid+1,r);
    pushup(u);
}
void update(int u,int l,int r,int ql,int qr,int add){
    if(ql>r||qr<l)return;
    if(ql<=l&&r<=qr){
        lz[u]+=add;
        mi[u]+=add;
        return;
    }
    int mid=(l+r)/2;
    pushdown(u);
    update(2*u,l,mid,ql,qr,add);
    update(2*u+1,mid+1,r,ql,qr,add);
    pushup(u);
}
int query(int u,int l,int r,int ql,int qr){
    if(ql>r||qr<l)return n+1;
    if(ql<=l&&r<=qr){
        return mi[u];
    }
    int mid=(l+r)/2;
    pushdown(u);
    return min(query(2*u,l,mid,ql,qr),query(2*u+1,mid+1,r,ql,qr));
}
struct Bit{
    int c[maxn];
    int lowbit(int x){
        return x&(-x);
    }
    void update(int x){
        while(x){
            c[x]++;
            x-=lowbit(x);
        }
    }
    int query(int x){
        int ret=0;
        while(x<=n){
            ret+=c[x];
            x+=lowbit(x);
        }
        return ret;
    }
}bit;
int main(){
    scanf("%d",&n);
    long long ans=0;
    for(int i=1;i<=n/2;i++){
        scanf("%d",&a[i]);
        ans+=bit.query(a[i]/2);
        bit.update(a[i]/2);
        rk[a[i]/2]=i;
    }
    build(1,1,n/2+1);
    for(int i=1;i<=n/2;i++){
        update(1,1,n/2+1,i+1,n/2+1,1);
    }
    for(int i=1;i<=n/2;i++){
        ans+=query(1,1,n/2+1,1,n/2+1);
        update(1,1,n/2+1,1,rk[i],1);
        update(1,1,n/2+1,rk[i]+1,n/2+1,-1);
    }
    printf("%lld\n",ans);
}

E
二分图带权最大匹配。

#include<bits/stdc++.h>
using namespace std;

int n,w[105][105],nx,ny;
const int INF=1e9;

struct dor{
    int x[4];
}a[105],b[105];


int getw(dor a, dor b){
    int ans=0;
    for(int i=0; i<4; i++){
        int flag=0;
        for(int j=0; j<4; j++){
            if(b.x[j]==a.x[i]) flag=1;
        }
        ans+=flag;
    }
    return ans;
}


void init(){
    scanf("%d",&n);
    nx=n;
    ny=n;
    for(int i=0; i<n; i++){
        for(int j=0; j<4; j++){
            scanf("%d",&a[i].x[j]);
        }
    }
    for(int i=0; i<n; i++){
        for(int j=0; j<4; j++){
            scanf("%d",&b[i].x[j]);
        }
    }
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++){
            w[i+1][j+1]=getw(a[i],b[j]);

            //cout<<i<<" "<<j<<": "<<w[i+1][j+1]<<endl;
        }
}

int linker[105],lx[105],ly[105];
bool visx[105],visy[105];
int d;

int dfs(int x)//完全匹配
{
    int y,tmp;
    visx[x] = 1;
    for(y = 1; y <= ny; y ++)
    {
        if(!visy[y])
        {
            tmp = lx[x] + ly[y] - w[x][y];
            if(!tmp)
            {
                visy[y] = 1;
                if(linker[y] == -1||dfs(linker[y]))
                {
                    linker[y] = x;
                    return 1;
                }
            }
            else if(d > tmp)//取最小的不在增广轨中的常数d
                d = tmp;
        }
    }
    return 0;
}


int KM()//求最大权匹配
{
    int sum,x,i,j;
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for(i = 1; i <= nx; i ++)
        for(j = 1,lx[i] = -INF; j <= ny; j ++)
            if(lx[i] < w[i][j])
                lx[i] = w[i][j];//初始化为权值最大的边的权值

    for(x = 1; x <= nx; x++)
    {
        while(1)
        {
            d = INF;//常数d每次都要进行初始化
            memset(visx,0,sizeof(visx));//每次dfs都要进行更新
            memset(visy,0,sizeof(visy));
            if(dfs(x))
                break;
            for(i = 1; i <= nx; i ++)
                if(visx[i])//在增广轨中的x点标减去常数d
                    lx[i] -= d;
            for(i = 1; i <= ny; i ++)
                if(visy[i])//在增广轨中的y点标加上常数d
                    ly[i] += d;
        }
    }
    sum = 0;
    for(i = 1; i <= ny; i ++)
        if(linker[i]!=-1)
            sum += w[linker[i]][i];
    return sum;
}

int main(){
    init();

    printf("%d\n",4*n-KM());
    return 0;
}

F
好像做烦了,写了一坨不知道什么的线段树。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=100003;
ll power(ll n,ll p){
    ll ans=1;
    ll base=n;
    while(p){
        if(p&1)ans=ans*base%mod;
        base=base*base%mod;
        p>>=1;
    }
    return ans;
}
ll inv(ll n){
    return power(n,mod-2);
}
int n;
int p[maxn],d[maxn],ord[maxn];
ll lz[maxn<<2],sum[maxn<<2];
void pushup(int u){
    sum[u]=(sum[2*u]+sum[2*u+1])%mod;
}
void pushdown(int u){
    if(lz[u]==1)return;
    sum[2*u]=sum[2*u]*lz[u]%mod;
    lz[2*u]=lz[2*u]*lz[u]%mod;
    sum[2*u+1]=sum[2*u+1]*lz[u]%mod;
    lz[2*u+1]=lz[2*u+1]*lz[u]%mod;
    lz[u]=1;
}
void build(int u,int l,int r){
    lz[u]=1;
    if(l==r){
        if(l==1){
            sum[u]=1;
        }
        else sum[u]=0;
        return ;
    }
    int mid=(l+r)/2;
    build(2*u,l,mid);
    build(2*u+1,mid+1,r);
    pushup(u);
}
ll query(int u,int l,int r,int ql,int qr){
    if(ql>r||qr<l)return 0;
    if(ql<=l&&r<=qr)return sum[u];
    int mid=(l+r)/2;
    pushdown(u);
    ll ret=query(2*u,l,mid,ql,qr);
    ret=(ret+query(2*u+1,mid+1,r,ql,qr))%mod;
    return ret;
}
void update1(int u,int l,int r,int pos,ll add){
    if(pos>r||pos<l)return;
    if(l==r){
        sum[u]=(sum[u]+add)%mod;
        return;
    }
    int mid=(l+r)/2;
    pushdown(u);
    update1(2*u,l,mid,pos,add);
    update1(2*u+1,mid+1,r,pos,add);
    pushup(u);
}
void update2(int u,int l,int r,int ql,int qr,ll mul){
    if(ql>r||qr<l)return;
    if(ql<=l&&r<=qr){
        sum[u]=sum[u]*mul%mod;
        lz[u]=lz[u]*mul%mod;
        return;
    }
    int mid=(l+r)/2;
    pushdown(u);
    update2(2*u,l,mid,ql,qr,mul);
    update2(2*u+1,mid+1,r,ql,qr,mul);
    pushup(u);
}
ll pw[100003];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&p[i],&d[i]);
        ord[i]=d[i];
    }
    ord[n+1]=0;
    sort(ord+1,ord+2+n);
    build(1,1,n+1);
    ll sum=0;
    pw[0]=1;
    for(int i=1;i<=n;i++)pw[i]=pw[i-1]*100%mod;
    for(int i=1;i<=n;i++){
        int pos=lower_bound(ord+1,ord+2+n,d[i])-ord;
        ll tmp=query(1,1,n+1,1,pos-1);
        tmp=tmp*p[i]%mod;
        sum=(sum+tmp*pw[n-i])%mod;
        update2(1,1,n+1,pos,n,100);
        update2(1,1,n+1,1,pos-1,100-p[i]);
        update1(1,1,n+1,pos,tmp);
    }
    sum=sum*inv(pw[n])%mod;
    printf("%lld\n",sum);
}

G
签到。

#include <bits/stdc++.h>
using namespace std;
int main(){
    int c,n;
    cin>>c>>n;
    n/=c;
    if(n==0)cout<<"-1\n";
    else if(n==1)cout<<1ll*c*c<<endl;
    else cout<<1ll*n*(n-1)*c*c<<endl;
}

H
先从后往前dp一下以第i个数开头的上升子序列个数,然后逐位确定。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const long double inf=1e18+0.5;
const long long INF=1e18+1.5;
int n;
ll k;
int a[500003];
int ord[500003];
ll dp[500003];
int ans[500003];
int tot=0;
ll c[500003];
int lowbit(int x){
    return x&(-x);
}
void update(int x,ll add){
    while(x){
        long double tmp=c[x];
        tmp+=add;
        if(tmp>inf)c[x]=INF;
        else c[x]=(long long)(tmp+0.5);
        x-=lowbit(x);
    }
}
ll query(ll x){
    ll ret=0;
    while(x<=500000){
        long double tmp=ret;
        tmp+=c[x];
        if(tmp>inf)return INF;
        else ret=(long long)(tmp+0.5);
        x+=lowbit(x);
    }
    return ret;
}
int main(){
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        ord[i]=a[i];
    }
    sort(ord+1,ord+1+n);
    for(int i=n;i>=1;i--){
        int pos=lower_bound(ord+1,ord+1+n,a[i])-ord;
        ll tmp=query(pos+1);
        tmp++;
        tmp=min(tmp,INF);
        dp[i]=tmp;
        update(pos,tmp);
    }
    bool flag=false;
    for(int i=1;i<=n;i++){
        if(a[i]<=a[ans[tot]]){
            continue;
        }
        if(k==1){
            ans[++tot]=i;
            flag=true;
            break;
        }
        if(dp[i]>=k){
            k--;
            ans[++tot]=i;
        }
        else {
            k-=dp[i];
        }
    }
    if(!flag){
        printf("-1\n");
    }
    else {
        printf("%d\n",tot);
        for(int i=1;i<=tot;i++)printf("%d%c",ans[i],i==tot?'\n':' ');
    }
}

I
枚举最左边的点所在的竖直线,记直线上点的个数是n。这条直线上最多取2个点,取2个点之后就不能取其他点了,这样的集合个数是(n2)(n2),要去除完全重合的点对的情况。如果取一个点,那这个点右上方可以取0或1个点,右下方可以取0或1个点,这样的集合个数是(p+1)(q+1)(p+1)∗(q+1),p、q分别为右上方和右下方的点的个数。按x从大到小排序,用树状数组维护一下即可。

#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
typedef long long ll;
struct node{
    int x,y;
}rec[100003];
bool cmp(node a,node b){
    if(a.x==b.x){
        return a.y<b.y;
    }
    return a.x>b.x;
}
int ord[100003];
int c1[100003];
int c2[100003];
int lowbit(int x){
    return x&(-x);
}
void update1(int x){
    while(x<=100000){
        c1[x]++;
        x+=lowbit(x);
    }
}
int query1(int x){
    int ret=0;
    while(x){
        ret+=c1[x];
        x-=lowbit(x);
    }
    return ret;
}
void update2(int x){
    while(x){
        c2[x]++;
        x-=lowbit(x);
    }
}
int query2(int x){
    int ret=0;
    while(x<=100000){
        ret+=c2[x];
        x+=lowbit(x);
    }
    return ret;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&rec[i].x,&rec[i].y);
        ord[i]=rec[i].y;
    }
    sort(ord+1,ord+1+n);
    sort(rec+1,rec+1+n,cmp);
    ll ans=0;
    for(int i=1,j;i<=n;i=j+1){
        j=i;
        while(j+1<=n&&rec[j+1].x==rec[j].x)j++;
        ll x=j-i+1;
        ans=(ans+x*(x-1)/2)%mod;
        ll tmp=0;
        for(int k=i;k<=j;k++){
            if(k!=i&&rec[k].y!=rec[k-1].y){
                ans=(ans-tmp*(tmp-1)/2+mod)%mod;
                tmp=1;
            }
            else tmp++;
        }
        ans=(ans-tmp*(tmp-1)/2+mod)%mod;
        for(int k=i;k<=j;k++){
            int pos=lower_bound(ord+1,ord+1+n,rec[k].y)-ord;
            ll tmp=1ll*(query1(pos-1)+1)*(query2(pos+1)+1)%mod;
            //cout<<"p: "<<rec[k].x<<' '<<rec[k].y<<' '<<pos<<endl;
            //cout<<query1(pos-1)<<' '<<query2(pos+1)<<endl;
            ans=(ans+tmp)%mod;
        }
        for(int k=i;k<=j;k++){
            int pos=lower_bound(ord+1,ord+1+n,rec[k].y)-ord;
            update1(pos);
            update2(pos);
        }
    }
    printf("%lld\n",ans);
}

J
签到。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll n,p2,p3;
    cin>>n>>p2>>p3;
    ll ans=1ll<<60;
    for(ll i=0;i<=2;i++){
        for(ll j=0;j<=2;j++){
            ll x=i*2+j*3;
            ll c=i*p2+j*p3;
            if(x>n)continue;
            ans=min(ans,c+(n-x+1)/2*p2);
            ans=min(ans,c+(n-x+2)/3*p3);
        }
    }
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值