Nordic Collegiate Programming Contest 2020解题报告(AMGCDJ)

根据解题顺序写的
A-Array of Discord

题意:修改一个数字里的一位,使序列变无序
思路:既然要改,就改成最小or最大,也就是0/1/9(注意不能出现前导0)

//-------这是用数字做的------用字符串也可以做,string可以直接比大小
ll a[105];
int n;
//是否满足要求(非按序)
bool check(int x){
    for(int i=max(1,x-2);i<min(n,x+2);i++){
        if(a[i] >a[i+1]) return 1;
    }
    return 0;
}
void solve(){
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        ll w=1;
        //修改成1/0/9 验证即可
        while(w<=a[i] || (w==1 && a[i]==0)){
            for(int j=0;;){
                if(w*10 > a[i] && j==0 && w>1){//第一位 且a[i]大于9
                    j=1;
                    continue;
                }
                //a[i]%w 剩下几位的数
                //(a[i]/w - (a[i]/w)%10 + j) 前几位的数 把w对应的位改成(0、1、9)
                ll x= (a[i]/w - (a[i]/w)%10 + j) * w + a[i]%w;
                ll y=a[i];
                a[i]=x;
                if(check(i)){
                    for(int k=1;k<=n;k++){
                        cout<<a[k]<<" ";
                    }
                    cout<<endl;
                    return;
                }
                a[i]=y;//失败的话 回到原样
               	if(j==0) j=1;
                else if(j==1) j=9;
                else  break;
            }
            w*=10;//每次×10 ,等于十进制往左一位
        }
    }
    cout<<"impossible"<<endl;
}

M-Methodic Multiplication

题意:用皮亚诺算两个自然数相乘
本场签到题,手算找规律系列
或者说就是自然数相乘,只要知道皮亚诺是每加一层S()就+1 就可以,直接x*y然后转化成皮亚诺的格式

void solve(){
	string s1,s2;
    cin>>s1>>s2;
    int cnt1=0,cnt2=0;
    for(int i=0;i<s1.size();i++){
        if(s1[i]=='S') cnt1++;
    }
    for(int i=0;i<s2.size();i++){
        if(s2[i]=='S') cnt2++;
    }
    string ans;
    int cnt=cnt2*cnt1;
    for(int i=0;i<cnt;i++){
        ans+="S(";
    }
    ans+="0";
    for(int i=0;i<cnt;i++){
        ans+=")";
    }
    cout<<ans<<endl;

}

G-Gig Combinatorics

题意:给定序列,问形如 “12…23"的子序列有多少种
注意到结尾必然是一个3,所以我们可以每遇到一个3就更新一次ans
当1、3固定时,设他们中间有k个2,可以知道有2^k-1个子序列满足要求,
即 2×2×2…×2×1-1

1212123

若序列如上,就有2×2×2×1-1 + 2×2×1-1 + 2×1-1 种子序列,
经过化简,有(((1×2)+1)×2+1)×2)- 3
再举个栗子

1221123

若序列如上,就有2×2×2×1-1 + 2×1-1 + 2×1-1 种子序列,
经过化简,有(((1×2)×2+1)+1 ×2)- 3

所以我们可以累计1的数量,每遇到一个3就减去此前遇到了的1的数量。
每遇到一个2,cnt×2;
遇到1时,cnt+1;

const int mod=1000000007;
void solve(){
	ll n;
	cin>>n;
    int p1=0;
    ll ans=0;
   int x;
    ll cnt=0;
    for(int i=1;i<=n;i++){
        cin>>x;
        if(x==1){
            cnt++;
            cnt%=mod;
            p1++;
        }
        else if(x==2){
            cnt*=2;
            cnt%=mod;
        }
        else{
            ans+=cnt;
            ans%=mod;
            ans=(ans-p1+mod);
            ans%=mod;
        }
    }
    cout<<ans<<endl;
    
}

C-Coin Stacks

题意:给n堆硬币,每次从不同的堆分别取一枚,问能不能取完,能的话给出每次取的堆。
思路:先考虑什么情况取不完,①每次取两枚,那必定取走的总数是偶数 ② 每次要从不同的堆里取,如果有一堆比其他所有加起来都多,最后肯定都会取到这一堆
我们可以贪心地 每次取最多的的两堆,此时要考虑怎么维护一个有序序列
然后注意到 n不超过50所有硬币加起来不超过1000 。成功的话是取总数/2次,那么也就是不超过500次。 直接用数组或者vector每次排序就可以
.
想过set,但是set是去重的,而我们这个会有重复,所以不适用orz

struct po{
    int num;
    int id;
}a[105];
bool cmp(po x, po y){
    return x.num>y.num;
}
int n;
void solve(){
	cin>>n;
    int sum=0,maxx=0;
    for(int i=1;i<=n;i++){
        cin>>a[i].num;
        a[i].id=i;
        sum+=a[i].num;
    }
    sort(a+1,a+1+n,cmp);
    maxx=a[1].num;
    if(sum%2 || maxx>sum/2) {
        cout<<"no\n";
        return;
    }
    cout<<"yes"<<endl;
    int p=1,pp=2;
    for(int i=1;i<=sum/2;i++){
        sort(a+1,a+1+n,cmp);
        cout<<a[p].id<<" "<<a[pp].id<<endl;
        a[p].num--;
        a[pp].num--;
       
    }
}

下班,剩下的等比赛结束的正答(趴
----------------------------补题分界线----------------------
D-Damsindistress

题意:给定每个大坝先有水量以及总容量,超过总容量时就会流向下一级(靠近根)的大坝。问最少需要多少新的水可以使整个大坝体系的根崩溃?
每个大坝都有它的下一级,从"树"的角度看也就是它的父亲,如果一个大坝i的父亲p[i]需要 f[p[i]] 的水量可以达成冲破营地的目标,那么 f[p[i]] 的水也可以是从i流向p[i],使它的父亲达成目标。
而i的水要流向p[i]要满足 : f[i] >= c[i] - u[i]
同时为了让p[i]可以冲破营地,要保证 : f[i]+u[i]>=f[p[i]
所以得到: f[i]=c[i]-u[i]+max(0,f[p[i]]-c[i])

ll p[400005],c[400005],u[400005],f[400005];
int main (){
	int n,w;
    cin>>n>>w;
    for(int i=1;i<=n;i++){
        cin>>p[i]>>c[i]>>u[i];
    }
    f[0]=w;
    ll ans=f[0];
    for(int i=1;i<=n;i++){
       f[i]=c[i]-u[i]+max(0ll,f[p[i]]-c[i]);
        ans=min(f[i],ans);
    }
    cout<<ans<<endl;
}

J-Joining Flows

题意:巧克力工厂有k个流出温度为t, 流量可在[a,b]间调节的巧克力水龙头。 其融合成的巧克力流量为每个水龙头的流量之和,在这里插入图片描述
温度为每个水龙头温度和流量的加权平均值,在这里插入图片描述
给出r种巧克力,问能否制成
题解: 显然我们有以下的结论:
①巧克力工厂的流量有最大值和最小值,不在此区间的肯定无法制成
②温度方面,温度加权式中ti是固定的,而xi可以分解成ai+ni。故可以提取出ai*ti的求和作为固定部分,浮动部分ni另行计算
③水龙头之间没有顺序之分,所以可以直接进行排序(温度),通过生序or降序求出剩余需要的流量可生成温度的最小值mint和最大值maxt
④根据温度加权的式子算出最后生成温度的最小值和最大值,若题目所给温度位于此区间则输出yes,反之输出no
不得不注意的是,记得转double


struct tap{
    int t,a,b;
}p[15];
bool cmp(tap x, tap y){
    return x.t<y.t;
}
int main(){
    int k,r;
    cin>>k;
    ll maxf=0,minf=0,mst=0;
    for(int i=1;i<=k;i++){
        cin>>p[i].t>>p[i].a>>p[i].b;
        maxf+=p[i].b;
        minf+=p[i].a;
        mst+=p[i].a*p[i].t;
    }
    
    sort(p+1,p+1+k,cmp);
    
    cin>>r;
    int t,f;
    while(r--){
        cin>>t>>f;
        if(f<minf || f>maxf){
            cout<<"no\n";
            continue;
        }
        double tmp=f-minf;
        double maxt=0,mint=0;
        for(int i=1;i<=k;i++){
            if(double(p[i].b - p[i].a) <= tmp){
                tmp-=double(p[i].b - p[i].a);
                mint+=p[i].t*double(p[i].b - p[i].a);
            }
            else{
                mint+=p[i].t*tmp;
                break;
            }
        }
        tmp=f-minf;
        for(int i=k;i>=1;i--){
            if(double(p[i].b - p[i].a) <= tmp){
                tmp-=double(p[i].b - p[i].a);
                maxt+=p[i].t*double(p[i].b - p[i].a);
            }
            else{
                maxt+=p[i].t*tmp;
                break;
            }
        }
        
        double minn=(mint+mst)/f;
        double maxx=(maxt+mst)/f;
        
        if(double(t)>=minn&&(double)t<=maxx){
            cout<<"yes\n";
        }
        else cout<<"no\n";
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值