COMPFEST 14 - Preliminary Online Mirror (Unrated, ICPC Rules, Teams Preferred) A、B、G、H、M(数学、构造、图论)

A. Accumulation of Dominoes 

 

签到,数砖块

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main()
{
    int n,m;cin>>n>>m;
    if(m==1)cout<<n-1<<endl;
    else cout<<n*(m-1)<<endl;
    return 0;
}

B. Basketball Together

题意:p数组是每个人的战力,每个队的战力是人数*队里的最高战力

敌队的战力是D,求能够组多少队

思路:

1.把战力从小到大排序,l 是左箭头,r 是右箭头。

2.将r作为最高战力时,l 消耗的人数是D/ p[r] 。

3.如果 l 加上消耗人数大于 r ,那么break。

代码:

#include<bits/stdc++.h>
using namespace std;
int p[100005];int n,d;
int main()
{
    cin>>n>>d;
    for(int i=0;i<n;i++)cin>>p[i];
    sort(p,p+n);
    int l=0,r=n-1;int ans=0;
    for(r=n-1;r>=l;r--){
        if(l+d/p[r]<=r){ans++;l+=d/p[r];}
        else break;
    }
    cout<<ans<<'\n';
    return 0;
}

G. Garage

题意:三角形一条直角边是a,一条斜边是b,求第n小的b^{2}-a^{2}

思路:

当b=a+1时,x=2*a+1,除了1之外的所有奇数都是符合的x

当b=a+2时,x=4*a+4,除了4之外的所有4的倍数都是符合的x

简单枚举一下

3 5 7

8 9 11

12 13 15……

那么可以直接列出公式解。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;cin>>n;
    if(n==1)cout<<3;
    else if(n%3==1)cout<<4*(n/3+1);
    else if(n%3==2)cout<<4*(n/3+1)+1;
    else if(n%3==0)cout<<4*(n/3)+3;
    return 0;
}

H. Hot Black Hot White

简单的找规律

已知按照计算规则有:

//    x y result

//    1 1 2
//    1 0 1
//    1 2 2
//    0 2 1
//    0 0 0
//    2 2 2

所以如果num0>=num1+num2,那么Z=2,优先01和02,剩下00。

否则 Z=0,只要不让0和0在一起就可以了

代码:【虽然代码有点长,但都是重复的段落】

#include<bits/stdc++.h>
using namespace std;
int a[100005];
stack<int>v[3];
int main()
{
    char s[100005];
    int n;cin>>n;int x,y; s[n]='\0';
    for(int i=0;i<n;i++){cin>>a[i];v[a[i]%3].push(i);}
    if(v[0].size()>=v[1].size()+v[2].size()){
        cout<<2<<endl;
        while(!v[1].empty()){
            x=v[1].top();y=v[0].top();
            v[1].pop();v[0].pop();
            s[x]='1';s[y]='0';
        }
        while(!v[2].empty()){
            x=v[2].top();y=v[0].top();
            v[2].pop();v[0].pop();
            s[x]='1';s[y]='0';
        }
        while(!v[0].empty()){
            x=v[0].top();v[0].pop();y=v[0].top();v[0].pop();
            s[x]='1';s[y]='0';
        }
    }
    else{
            cout<<0<<endl;
        while(!v[0].empty()){
            if(!v[2].empty()){
                 x=v[0].top();v[0].pop();y=v[2].top();v[2].pop();
                  s[x]='1';s[y]='0';
            }
            else if(!v[1].empty()){
                 x=v[0].top();v[0].pop();y=v[1].top();v[1].pop();
                  s[x]='1';s[y]='0';
            }
        }
        while(!v[1].empty()){
            if(!v[2].empty()){
                 x=v[1].top();v[1].pop();y=v[2].top();v[2].pop();
                  s[x]='1';s[y]='0';
            }
            else if(!v[1].empty()){
                 x=v[1].top();v[1].pop();y=v[1].top();v[1].pop();
                  s[x]='1';s[y]='0';
            }
        }
    while(!v[2].empty()){
            if(!v[1].empty()){
                 x=v[2].top();v[2].pop();y=v[1].top();v[1].pop();
                  s[x]='1';s[y]='0';
            }
            else if(!v[2].empty()){
                 x=v[2].top();v[2].pop();y=v[2].top();v[2].pop();
                  s[x]='1';s[y]='0';
            }
        }
    }
    cout<<s<<endl;
    return 0;
}

M. Moving Both Hands

题意:找到从1和k走到相同节点的最少花费

思路:

1. 最少花费是 dis(1 , v) + dis(k , v) 【其中v是1~n的任意一个数】

2.设有向图都反向,dis'(v , k)= dis(k , v)

3.所以最少花费是dis(1,v)+dis'(v , k)

4.因为正向图和反向图接在一起了,所以dis''(1,k)=dis(1,v)+dis'(v , k)

直接dijstra就可以了

dijstra首先找到连接u的所有边,更新u到v的所有距离,并且把他们都放到heap里面按 花费 排序。之后取排序中花费最小的那个点x,找到连接x的所有vx,更新 通过点x 能使 vx到点u最短,更新之后再放到堆排序中

尽管 i 和 i+n 之间的距离是0,但是 dis[i] 和 dis[i+n] 都可能是答案,可能因为 i 和 1先更新,那么之后遇到 i+n 更新后,想要在更新 i 就发现 i 已经更新过了,所以答案就在dis[i+n]里面而不在dis[i]里面。所以在最终求答案的时候找最小值。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
typedef pair<long long,long long> pll;
int dis[400005],vis[400005];int n,m;
 vector<pll>mp[400005];
void dijkstra(int u)
{
    for(int i=2;i<=2*n;i++)dis[i]=inf; memset(vis,0,sizeof vis);dis[u]=0;
    priority_queue<pll, vector<pll>,greater<pll> >heap;
    heap.push({0,u});
    while (heap.size()){
        pll t = heap.top();heap.pop();
        int ver = t.second;if (vis[ver]) continue;vis[ver]=true;
        for(auto k:mp[ver]){
            if(!vis[k.second]&&dis[k.second]>dis[ver]+k.first){
                dis[k.second]=dis[ver]+k.first;
                heap.push({dis[k.second],k.second});
            }
        }
    }
}
signed main()
{
        cin>>n>>m;
        for(int i=1;i<=n;i++){mp[i].push_back({0,i+n});}
        for(int i=0;i<m;i++){
            int u,v,w;cin>>u>>v>>w;
            mp[u].push_back({w,v});
            mp[v+n].push_back({w,u+n});
        }
        dijkstra(1);
        for(int i=2;i<=n;i++){
            if(dis[i]!=inf||dis[i+n]!=inf)cout<< min(dis[i],dis[i+n])<<' ';
            else cout<<-1<<' ';
        }
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值