CCPC - CometOJ 2019夏季欢乐赛 题解

A 完全k叉树

题意

已知完全\(k\)叉树有\(n\)个节点,求树上两个最远点的距离,节点之间的距离均为1。

基本思路

求树的深度\(h\),则满\(k\)叉树的两点距离为\((h-1)*2\),对剩余一层的节点数\(rest\)做判断,若\(rest>k^{h-1}/k\)则ans+2,否则若\(rest>0,ans+1\)

#include<bits/stdc++.h> 
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
using namespace std;
int main()
{
    ll count=0,t,n,k;cin>>t;
    while(t--)
    {
        count=0;ll h=0;
        cin>>k>>n;
        if(k==1){
            cout<<n-1<<endl;continue;
        }
        ll sum=0,tmp=1;
        while(1)
        {
            sum+=tmp;
            tmp*=k;
            h++;
            if(tmp+sum>=n)break; 
        }
        ll ans=(h-1)*2;
        n=n-sum;
        if(n>tmp/k)
        {
            ans+=2;
        }
        else if(n>=1)
        {
            ans++;
        }
        
        cout<<ans<<endl;
        
    }
    return  0;
}

B 距离产生美

题意

一串数,求使的所有相邻数的差\(>=k\)的最小修改数

基本思路

差分,然后扫描,若扫到某个间隔\(<k\)则记\(cnt++,i++\)
注意绝对值

#include<bits/stdc++.h> 
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
using namespace std;
int arr[MAXN],brr[MAXN];
int main()
{   
    ll n,k,cnt=0;cin>>n>>k;
     FOR(i,0,n){
        cin>>arr[i];
        if(i!=0)brr[i]=arr[i]-arr[i-1];
     }
     FOR(i,1,n)
     {
        if(abs(brr[i])<k)
         {
            cnt++;
            i++;
         }
     }
     cout<<cnt<<endl;
    return 0;
}

C 烤面包片

基本题意

\(n!!!\)\(k\)模的结果

基本思路

若某次求累乘的时候,\(n>=mod\)则结果必为0,因此循环必定不会超过\(k\)

#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
using namespace std;
int main()
{
    ll n,mod,ans=1;
    cin>>n>>mod;
    if(n>=mod)
    {
        cout<<0<<endl;return 0;
    }
    FOR(i,1,n+1)
    {
        ans=(ans*i);
        if(ans>=mod)
        {
            cout<<0<<endl;return 0;
        }
    }
    n=ans;ans=1;
    FOR(i,1,n+1)
    {
        ans=(ans*i);
        if(ans>=mod)
        {
            cout<<0<<endl;return 0;
        }
    }
    n=ans;ans=1;
    FOR(i,1,n+1)
    {
        ans=(ans*i)%mod;
    }
    cout<<ans<<endl;
    return 0;
}

F 三元组

题意

\(n\)个三元组\((a_i,b_i,c_i)\)若一对\([i,j](i<=j)\),满足\(2*min(a_i+a_j,b_i+b_j)\le{max(a_i+a_j,b_i+b_j)}\)则得到c_i*c_j的钱,输出求和模1e9+7的值

基本思路

每个三元组增加一个属性\(2*a_i-b_i\),对此属性排序,求\(c_i\)的前缀和sum,然后手动二分求\(max(posr),使得(2*a_1+2*a_{posr}-b_1-b_{posr}<=0)\)
\([1,posr]\)选择起点,然后从后往前扫描确定终点,因为若\([i,j]\),则必有\([i+1,k](k<=j)\);找到后,用\(c_i*sum[i,j]\)求出此次增加的钱。要两次排序。

#include<bits/stdc++.h> 
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
#define MOD (1000000000+7)
#define debug cout<<"debug"<<endl
using namespace std;
typedef struct{
    ll a,b,c,trans;
}NODE;NODE nodes[MAXN];
ll n,sum[MAXN];
bool cmp(NODE n1,NODE n2)
{
    return n1.trans<n2.trans;
}
ll solve()
{
    sort(nodes+1,nodes+n+1,cmp);
    sum[1]=nodes[1].c;
    FOR2(i,2,n)
    {
        sum[i]=nodes[i].c+sum[i-1];
    }
    ll l=1,r=n,posr=0;
    while(l<=r)
    {   
        ll mid=(l+r)/2;
        if(nodes[mid].trans+nodes[1].trans>0){
            r=mid-1;
        } 
        else {
            posr=mid;l=mid+1;
        }
    }
    ll res=0;
    for(l=1,r=posr;l<=r;l++)
    {//确定起点     
        while(l<=r&&nodes[l].trans+nodes[r].trans>0) {
            r--;
            posr--;
        }
        if(r<l) break;
        res=(res+((sum[r]-sum[l-1])%MOD)*nodes[l].c)%MOD; //L~R的和 
    }
    return res; 
}
int main()
{
    cin>>n;
    FOR2(i,1,n)
    {
        cin>>nodes[i].a>>nodes[i].b>>nodes[i].c;
        nodes[i].trans=2*nodes[i].a-nodes[i].b;
    }
    ll ans=solve();
    FOR2(i,1,n)
    {
        nodes[i].trans=2*nodes[i].b-nodes[i].a;
    }
    ans+=solve();
    cout<<ans%MOD<<endl;
    
    return 0;
}

G 篮球校赛

题意

n个人,每人5个属性,求选出5人的最大属性和,每个属性只能选择一人。

基本思路

使用插排,选出每个属性排名前五的人,然后dfs暴搜

#include<bits/stdc++.h> 
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
using namespace std;
typedef struct{
    int state[6];
}NODE;NODE nodes[MAXN];
int maxp[6][6];
int vis[MAXN];
ll ans=0;int n;
void dfs(ll level,ll res)
{
    if(level==6)
    {
        ans=max(ans,res); 
        return;
    }
    else {
        for(int i=1;i<=5;i++)
        {
            if(vis[maxp[i][level]]==1)continue;
            vis[maxp[i][level]]=1;//记录选择的队员 
            dfs(level+1,res+nodes[maxp[i][level]].state[level]);
            vis[maxp[i][level]]=0;
        }
    }
}
int main()
{
    cin>>n;
    FOR2(i,1,n)
    {
        for(int j=1;j<=5;j++)
        {
            cin>>nodes[i].state[j];
            for(int k=1;k<=5;k++)
            {//逐个与5个元素比较 
                if(nodes[i].state[j]>nodes[maxp[k][j]].state[j])
                {//插入排序 
                    for(int h=5;h>=k+1;h--)
                        maxp[h][j]=maxp[h-1][j];
                    maxp[k][j]=i;
                    break;
                }   
                
            }
        }
    }
    for(int i=1;i<=5;i++)
    {
        vis[maxp[i][1]]=1;
        dfs(2,nodes[maxp[i][1]].state[1]);
        vis[maxp[i][1]]=0;
    }
    cout<<ans<<endl; 
    return 0;
}

H 分配学号

题意

给一串数,求使得他们互不相同的需要增加的最小数和,每个数只能增加不能减。

基本思路

排序,然后求出每个人可选择的数量,即能上升的最小数量(若小于前一个,则最少要上升到比前一个+1),\(max(a[i-1]+1,a[i])-a[i]+1\),注意不变也是一种选择,然后累乘。

#include<bits/stdc++.h> 
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
#define MOD (1000000000+7)
using namespace std;
ll n,arr[MAXN],brr[MAXN];
int main()
{
    cin>>n;
    FOR2(i,1,n)
    {
        cin>>arr[i];
    }
    sort(arr+1,arr+1+n);
    ll ans=1;
    FOR2(i,1,n)
    {
        if(i!=1)
        {
            brr[i]=max(arr[i-1]+1,arr[i])+1-arr[i];
            arr[i]=max(arr[i-1]+1,arr[i]);
            ans=(ans*brr[i])%MOD;
        }
            
    }
    cout<<ans%MOD<<endl;
    return 0;
}

I Gree的心房

题意

给n*m的矩阵和k个箱子,箱子随意摆放,求从左上到右下的最短路径长,若不能达到输出-1;

基本思路

k最大不超过(n-1)(m-1),路径为n+m-2

#include<bits/stdc++.h> 
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FOR2(i,a,b) for(int i=a;i<=b;i++)
#define sync ios::sync_with_stdio(false);cin.tie(0) 
#define ll long long
#define MAXN 100010
using namespace std;
int main()
{
    ll n,m,k;cin>>n>>m>>k;
    if(k>(n-1)*(m-1)) {
        cout<<-1<<endl; 
    }
    else{
        cout<<n+m-2<<endl;
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/tldr/p/11263186.html