bistuacm 2019年第四场进阶训练赛题解

比赛链接:http://citel.bjtu.edu.cn/vjudge/contest/view.action?cid=314#overview
读题链接:https://vjudge.net/contest/295670
(因vjudge经常崩溃而无法交题,而北交平台偶尔题目乱码,因此用这种形式)
难度:cf 1300~1800原题

A

知识点:模拟
题意:先求出所有数的和sum,然后从左到右找到前缀和大于等于sum的一半即可。

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

int main(){
    int n;
    cin>>n;
    int a[222222];
    long long sum=0,s=0,i;
    for(i=0;i<n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    for(i=0;i<n;i++){
        s+=a[i];
        if(s*2>=sum)break;
    }
    cout<<i+1;
}

B

知识点:字符串,暴力枚举
题意:一次操作可添加一个字符,或copy已有字符串(一共只能copy一次)问最少操作数。
解法:由于n很小(只有100),因此可O(n^2)暴力查找最大的a(0~ i)=a(i+1~2i)的相同子串。

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

int main(){
    int n;
    cin>>n;
    char a[222];
    cin>>a;
    int i,j,k,m=0;
    for(j=1;j<=n/2;j++){
        for(k=0;k<j;k++){
            if(a[k]!=a[j+k])break;
        }
        if(k==j)m=max(m,j);
    }
    cout<<min(n-m+1,n);
}

C

知识点:贪心
题意:从左到右依次可翻转第1个到第i个部分(前缀字符串),或选择不翻转。问怎样操作可让最终字典序最小
解法:显然aaaa…aabb…bb这样的字符串是字典序最小的。遍历整个字符串,遇到a[i]!=a[i+1]的情况翻转前i个即可。最后检查a[n-1],若是a则需要再翻转一次。

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

int main(){
    int i;
    char a[1111];
    cin>>a;
    int jud,z=0;
    for(i=0;a[i+1]!='\0';i++){
        if(a[i+1]!=a[i]){
            cout<<1,jud++;
        }
        else cout<<0;
         cout<<" ";
    }
    if(a[i]=='a')cout<<"1";
    else cout<<"0";
}

D

知识点:树形dp,dfs
题意:对于给定有根树,对每个叶子染色,对于i从1到n,求快乐节点不小于i个的最小染色的颜色数量k。所谓快乐节点指:以该节点为根的子树,所有叶子的颜色不同。
解法:设叶子总数为m。显然,i=n时k为m,i≤p时k为1。这时可以观察到,假设所有叶子的权值为1,每个节点的权值为它所有孩子权值之和,那么这个权值就是该节点作为“快乐节点”的最小染色数。因此可以用一个dp[i]表示第i个节点的权值,然后dfs求所有节点权值,最后排序后依次从小到大输出即可。

#include<bits/stdc++.h>
using namespace std;
vector<int>g[111111];
int dp[111111]={0};
void dfs(int x){
    if(g[x].size()==0)dp[x]=1;
    for(int i=0;i<g[x].size();i++){
        dfs(g[x][i]);
        dp[x]+=dp[g[x][i]];
    }
}
int main(){
    int n,i;
    cin>>n;
    for(i=2;i<=n;i++){
        int x;
        cin>>x;
        g[x].push_back(i);
    }
    dfs(1);
    sort(dp+1,dp+1+n);
    for(i=1;i<=n;i++)cout<<dp[i]<<" ";
}

E

知识点:贪心
题意:相邻两数x和y合并,新数可选择为x-y或y-x。求最终只剩一个数的最大值。
解法:显然,若所有数有负有正,那么sum(abs(a[i]))即为所求。若全为正或全为负,则要牺牲绝对值最小的那个数,最终的sum-2min(abs)即可。
注意要处理n=1的特解。

#include<bits/stdc++.h>
using namespace std;
long long a[555555];
int main(){
    long long n,i,j1=0,j2=0,m1=1e10,m2=-1e10,sum=0;
    cin>>n;
    for(i=0;i<n;i++){
        scanf("%lld",&a[i]);
        sum+=abs(a[i]);
        if(a[i]>=0)j1=1;
        if(a[i]<=0)j2=1;
        m1=min(m1,a[i]);
        m2=max(m2,a[i]);
    }
    if(n==1){cout<<a[0];return 0;}
    if(j1&&j2)cout<<sum;
    else if(j1){
        cout<<sum-2*m1;
    }
    else cout<<sum+2*m2;
}

F

知识点:数学,数论
题意:一个循环节为k的循环字符串(由+和-组成)来控制加或减。然后求∑(i=0~n) si*a^(n−i) *b^i
解法:首先可以把前k项求出来。对于长度为n+1的循环和,循环节数量为(n+1)/k。这时可以看到,对于所有的循环节,对应相同位置的项可以组成公比为(b/a)k的等比数列。这样根据项数(即循环节数量)和首项(即前k项,均是第一个循环节中的数),就可以用a0*(qm-1)/(q-1)计算出和了。
计算过程中可以用a的逆元来表示1/a。
要注意特判公比q=1的情况

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

#define mod 1000000009

long long inv(long long a){
    if(a==1)return 1;
    return inv(mod%a)*(mod-mod/a)%mod;
}
long long power(long long a,long long b){
    long long res=1;
    while(b){
        if(b&1)res=res*a%mod;
        b/=2;
        a=a*a%mod;
    }
    return res;
}
long long c[111111];
char in[111111];
int main(){
    int n,a,b,k,i;
    cin>>n>>a>>b>>k;
    cin>>in;
    c[0]=power(a,n);
    for(i=1;i<k;i++){
        c[i]=c[i-1]*b%mod*inv(a)%mod;
    }
    for(i=0;i<k;i++){
        if(in[i]=='-')c[i]=mod-c[i];
    }
    long long sum=0;
    int chu=(n+1)/k;
    long long q=power(b,k)*power(inv(a),k)%mod;
    if(q==1){
        for(i=0;i<k;i++){
            sum=sum+chu*c[i];
        }
        cout<<(sum+mod)%mod;
        return 0;
    }
    for(i=0;i<k;i++){
        sum=(sum+c[i]*(power(q,chu)-1)%mod*inv(q-1)%mod)%mod;
    }
    cout<<sum;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值