“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛)

“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛)


A.点对最大值

题意:

在这里插入图片描述

解法:

起始就是dp求树的直径的变形题,但是我用d[x][2]存最大和次大,用d[x][0]+d[x][1]更新答案只能过百分60
改成d[x]记录最大,用d[x]+d[v]+w更新答案就对了

琢磨了一下感觉是d[x][2]的方法没办法判断是否是两个点(一个点的时候也会被计算进去)

ps:
我好像被我自己这种写法坑过一次了,又上当一次。

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
vector<pair<int,int> >g[N];
int a[N],n;
int d[N];//向下延伸只含一个端点
int ans;
void dfs(int x,int fa){
    d[x]=a[x];//fir
    for(auto i:g[x]){
        int v=i.first,w=i.second;
        if(v==fa)continue;
        dfs(v,x);
        ans=max(ans,d[x]+d[v]+w);
        d[x]=max(d[x],d[v]+w);
    }
}
signed main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++){
            g[i].clear();
        }
        for(int i=2;i<=n;i++){
            int x,w;cin>>x>>w;
            g[x].push_back({i,w});
            g[i].push_back({x,w});
        }
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        ans=-1e18;
        dfs(1,1);
        cout<<ans<<endl;
    }
    return 0;
}

B.减成一

题意:

在这里插入图片描述

数据范围:n<=1e5

解法:

每次操作可以将一个区间内的数减一。
可以将操作转化为差分数组操作,在差分数组中,操作可以转为先选取一个数减一再选取一个数加一。
那么问题变为要将差分数组第一个数修改为1,其余数为0,问最少操作次数。
最快操作方式就是将差分数组第一个数减为1,其余减为0。
因为保证有解,所以答案为差分数组的正数之和减一。

ps:
转化为差分数组似乎有点降维的意思

code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],n;
int b[N];
signed main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=n;i++){
            b[i]=a[i]-a[i-1];
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            if(b[i]>0){
                ans+=b[i];
            }
        }
        cout<<ans-1<<endl;
    }
    return 0;
}

D.扔硬币

题意:

在这里插入图片描述
在模1e9+7意义下计算答案

数据范围:n<=1e5,k<=1e3,k<=n

解法:

已知至少m个反面,恰好k个正面,那么至少m+k个是确定的,如果m+k>n,那么无解,直接输出0

这题的概率选择用合法方案除以总方案来计算
合法方案为组合数C(n,k)
因为题目已知至少有m枚是反面,所以总方案数为2n-sum(C(n,i)),其中i<m,减去sum(C(n,i))是减去少于m个是反面的情况

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1e5+5;
int fac[N];
int inv[N];
int n,m,k;
int ppow(int a,int b,int mod){
    int ans=1%mod;a%=mod;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int C(int n,int m){
    if(m<0||m>n)return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
    fac[0]=1;
    for(int i=1;i<N;i++){
        fac[i]=fac[i-1]*i%mod;
    }
    inv[N-1]=ppow(fac[N-1],mod-2,mod);
    for(int i=N-2;i>=0;i--){
        inv[i]=inv[i+1]*(i+1)%mod;
    }
    int T;cin>>T;
    while(T--){
        cin>>n>>m>>k;
        if(m+k>n){
            cout<<0<<endl;
            continue;
        }
        int ans=ppow(2,n,mod);
        for(int i=0;i<m;i++){
            ans=(ans-C(n,i)+mod)%mod;
        }
        ans=C(n,k)*ppow(ans,mod-2,mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}

G.养花

题意:

在这里插入图片描述
在这里插入图片描述

解法:

采用网络流直接进行建图,对于四种操作,每次都新建两个点from和to,
from->to边权为c
[a1,a2]区间每个节点连接到from节点,边权为inf
to连接[b1,b2]区间的每个节点,边权为inf
跑最大流即可。

ps:
如果数据范围大的话,还需要使用线段树来优化区间建图

code:
#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int N=2e4+5;
const int M=1e6+5;
int head[N],nt[M],to[M],w[M],cnt;
int idx[N],tot;
int d[N];
int n,m,k;
void init(){
    memset(head,0,sizeof head);
    cnt=1;
    tot=0;
}
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
bool bfs(int st,int ed){
    for(int i=1;i<=tot;i++)d[i]=0;
    d[st]=1;
    queue<int>q;
    q.push(st);
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=nt[i]){
            int v=to[i];
            if(w[i]&&!d[v]){
                d[v]=d[x]+1;
                q.push(v);
                if(v==ed)return 1;
            }
        }
    }
    return 0;
}
int dfs(int x,int ed,int flow){
    if(x==ed)return flow;
    int res=flow;
    for(int i=head[x];i;i=nt[i]){
        int v=to[i];
        if(w[i]&&d[v]==d[x]+1){
            int k=dfs(v,ed,min(res,w[i]));
            w[i]-=k;
            w[i^1]+=k;
            res-=k;
            if(!k)d[v]=-1;
            if(!res)break;
        }
    }
    return flow-res;
}
int dinic(int st,int ed){
    int ans=0;
    while(bfs(st,ed))ans+=dfs(st,ed,1e9);
    return ans;
}
signed main(){
    int T;cin>>T;
    while(T--){
        init();
        cin>>n>>m>>k;
        int st=++tot;//源点
        int ed=++tot;//汇点
        for(int i=1;i<=k;i++){
            idx[i]=++tot;
        }
        //k高度连接汇点
        add(idx[k],ed,1e9);//k->ed容量无限
        add(ed,idx[k],0);
        //
        for(int i=1;i<=n;i++){
            int hi;cin>>hi;
            add(st,idx[hi],1);
            add(idx[hi],st,0);
        }
        while(m--){
            int op,c;cin>>op>>c;
            int from=++tot;
            int to=++tot;
            add(from,to,c);
            add(to,from,0);
            if(op==1){//l->r
                int l,r;cin>>l>>r;
                add(idx[l],from,1e9);
                add(from,idx[1],0);
                add(to,idx[r],1e9);
                add(idx[r],to,0);
            }else if(op==2){//[l,r]->x
                int l,r,x;cin>>l>>r>>x;
                for(int i=l;i<=r;i++){
                    add(idx[i],from,1e9);
                    add(from,idx[i],0);
                }
                add(to,idx[x],1e9);
                add(idx[x],to,0);
            }else if(op==3){//x->[l,r]
                int x,l,r;cin>>x>>l>>r;
                add(idx[x],from,1e9);
                add(from,idx[x],0);
                for(int i=l;i<=r;i++){
                    add(to,idx[i],1e9);
                    add(idx[i],to,0);
                }
            }else if(op==4){//[l,r]->[lc,rc]
                int l,r,lc,rc;cin>>l>>r>>lc>>rc;
                for(int i=l;i<=r;i++){
                    add(idx[i],from,1e9);
                    add(from,idx[i],0);
                }
                for(int i=lc;i<=rc;i++){
                    add(to,idx[i],1e9);
                    add(idx[i],to,0);
                }
            }
        }
        int ans=dinic(st,ed);
        cout<<ans<<endl;
    }
    return 0;
}

I.字典序

题意:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解法:

对于a[i]位置的数据分一下三种情况进行讨论:
1.若a[i] == a[i+1],那么删除a[i]后与删除a[i + 1]的结果一样,那么a[i]出处的字典序会比a[i+1]出小,
2.若a[i] < a[i+1],那么删除a[i]后的数组在第i位上比删除a[i+1]所得到的更大,所以第i个位置的字典序比后面位置的字典序都小
3. 若a[i] > a[i+1],那么删除a[i]后的数组在第i位上比删除a[i+1]所得到的更小,所以第i个位置的字典序比后面位置的字典序都大。

上述情况的处理有一个很好的办法:
定义一个双端队列deque
从后向前遍历,如果i比后面的都小,就放到队尾;如果i比后面的都大,就放到队头。
但是发现等于的情况在这里不好处理,因为要插入队列中间,
不过因为等于的几个数直接从小到大输出就行了,那么将等于的合并,输出的时候再将合并的下标从小到大输出就行了。

code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
vector<int>g[N];
int a[N];
signed main(){
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=n;i++){
            g[i].clear();
        }
        deque<int>q;
        q.push_front(n);
        for(int i=n-1;i>=1;i--){
            if(a[i]==a[i+1]){//将等于的合并,输出的时候从小到大输出就行了
                int j=i;
                while(j>=1&&a[j]==a[i]){
                    g[i+1].push_back(j);//合并到i+1
                    j--;
                }
                j++;
                i=j;
            }else if(a[i]>a[i+1]){//i<后面全部
                q.push_back(i);
            }else if(a[i]<a[i+1]){//i>后面全部
                q.push_front(i);
            }
        }
        while(!q.empty()){
            int x=q.back();
            q.pop_back();
            int len=g[x].size();
            for(int i=len-1;i>=0;i--){
                cout<<g[x][i]<<' ';
            }
            cout<<x<<' ';
        }
        cout<<endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值