2019BAPC补题


题目和题解地址

Problem A: Appeal to the Audience

在这里插入图片描述
在这里插入图片描述
徐州热身赛的题目
长链剖分

建一个优先队列,对于每个非根节点将其值置为重儿子的值+1,所有轻儿子的值入队,然后将根节点的值-1加入队列与排序后的快乐值进行匹配

#include<bits/stdc++.h>
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const ll MAXN=1e5+10;
ll cnt;
struct Edge{
    ll to,next;
}edge[MAXN];
ll head[MAXN],dp[MAXN],val[MAXN],heavy[MAXN];

void add(ll u,ll v){
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

priority_queue<ll>q;

void dfs(ll u){
    dp[u]=1;
    for(ll i=head[u];~i;i=edge[i].next){
        ll v=edge[i].to;
        dfs(v);
        if(dp[v]+1>dp[u]){
            dp[u]=dp[v]+1;
            heavy[u]=v;
        }
    }
    for(ll i=head[u];~i;i=edge[i].next){
        ll v=edge[i].to;
        if(v==heavy[u])continue;
        q.push(dp[v]);
        //de(dp[v]);
    }
}
int main(){
    memset(head,-1,sizeof(head));
    ll n,k;
    scanf("%lld%lld",&n,&k);
    for(ll i=1;i<=k;i++){
        scanf("%lld",&val[i]);
    }
    sort(val+1,val+k+1);
    ll u;
    for(ll v=1;v<n;v++){
        scanf("%lld",&u);
        add(u,v);
    }
    dfs(0);
    q.push(dp[0]-1);
    ll ans=0;
    for(ll i=k;i>0;i--){
        ll x=q.top();
        //de(x);
        q.pop();
        //de(val[i]);
        ans+=x*val[i];
    }
    printf("%lld\n",ans);
}
/*
5 3
5 4 3
0 0 1 1
*/

Problem B: Breaking Branches

在这里插入图片描述
签到题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    ll n;
    scanf("%lld",&n);
    if(n&1){
        printf("Bob\n");
    }
    else{
        printf("Alice\n1\n");
    }
}

Problem C: Conveyor Belts

在这里插入图片描述
在这里插入图片描述
构造题,个人认为这套题最难的题目,标程只有30行的代码orz
做法是先将a:b凑成1:1,在凑成c:d
利用类似二叉树的原理吧,好难写
c++高效位运算函数: _builtin
bitset的基本用法

#include<bits/stdc++.h>
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
ll remain[3],from[3],to[6];
ll a,b,c,d;
void connect(ll from,ll l,ll r){
    printf("%lld %lld\n",from*3+1,from*3+2);
    printf("%lld %lld\n",3*from,l>0?l*3:l);
    printf("%lld %lld\n",r>0?r*3:r,3*from);
}
int main(){
    scanf("%lld%lld",&a,&b);
    scanf("%lld%lld",&c,&d);
    ll cnt=1;
    while(cnt<c+d){
        cnt*=2;
    }
    remain[0]=d,remain[1]=c,remain[2]=cnt-d-c;
    ll ans=3*(__builtin_popcountll(remain[0])+__builtin_popcountll(remain[1])+__builtin_popcountll(remain[2])-1);
    printf("%lld\n",ans);
    ll last=0,cur=1;
    while(last<cur){
        cnt>>=1;
        ll out=0;
        ll add=0;
        for(ll i=0;i<3;i++){
            if(remain[i]>=cnt){
                remain[i]-=cnt;
                to[out++]=i-2;
            }
        }
        while(out<2*(cur-last)){
            to[out++]=cur+(add++);
        }
        for(ll i=0;i<cur-last;i++){
            connect(last+i,to[2*i],to[2*i+1]);
        }
        last=cur;
        cur=cur+add;
    }
    return 0;
}

Problem D: Deck Randomisation

在这里插入图片描述
在这里插入图片描述
扩展中国剩余定理
对于所有的情况都满足第一种情况,但有可能有序列存在第二种也满足的情况。对于一个序列我们要判断是否对于它的所有循环节都存在满足第二种情况的情况,如果都存在最后flag为true,则两种情况都需要考虑,最后取最小值,否则flag为false,只需考虑第一种情况

import java.util.*;
import java.math.*;
public class Main {
   static int MAXN=100010;
   static int[] a=new int[MAXN];
   static int[] b=new int[MAXN];
   static int[] ab=new int[MAXN];
   static int[] vis=new int[MAXN];
   static BigInteger[] m=new BigInteger[MAXN];
   static BigInteger[] r=new BigInteger[MAXN];
   static int num;
   static Vector<Vector<Integer>> xhj=new Vector<Vector<Integer>>();
   static BigInteger d,x,y;
    public static void Ex_gcd(BigInteger a,BigInteger b){
           if(b.compareTo(BigInteger.ZERO)==0){
               d=a;
               x=BigInteger.ONE;y=BigInteger.ZERO;
           }
           else{
               Ex_gcd(b,a.mod(b));
               BigInteger tmp;
               tmp=x;
               x=y;
               y=tmp.subtract(a.divide(b).multiply(y));
           }
       }
   public static BigInteger Ex_Crt()
   {
       BigInteger a = m[1],rr=r[1];
       for(int i=2;i<=num;++i)
       {
           BigInteger b = m[i];
           BigInteger c = r[i].subtract(rr);
           Ex_gcd(a,b);
           c = c.divide(d);
           b = b.divide(d);
           x = x.multiply(c).mod(b).add(b).mod(b);
           BigInteger lcm = a.multiply(b);
           rr = x.multiply(a).mod(lcm).add(rr).mod(lcm);
           a = lcm ;
       }
       return rr.equals(BigInteger.ZERO)? a:rr;
   }
   public static void main(String agvs[]) {
      Scanner input=new Scanner(System.in);
      int n=input.nextInt();
      for(int i=1;i<=n;i++) {
         a[i]=input.nextInt();
      }
      for(int i=1;i<=n;i++) {
         b[i]=input.nextInt();
      }
      for(int i=1;i<=n;i++) {
         ab[i]=a[b[i]];
      }
      int cnt=0;
      BigInteger n1=BigInteger.ONE;
      for(int i=1;i<=n;i++) {
         if(vis[i]==0) {
            xhj.add(new Vector<Integer>());
            int cur=i;
            int l=0;
            while(vis[cur]==0) {
               vis[cur]=1;
               xhj.get(cnt).add(cur);
               l++;
               cur=ab[cur];
            }
            cnt++;
            n1=n1.multiply(BigInteger.valueOf(l)).divide(n1.gcd(BigInteger.valueOf(l)));
         }
      }
      
      boolean flag=true;
      for(int i=0;i<xhj.size();i++) {
         int sz=xhj.get(i).size();
         for(int j=0;j<sz;j++) {
            flag=true;
            for(int k=0;k<sz;k++) {
               if(a[xhj.get(i).get(k)]!=xhj.get(i).get((j+k)%sz)) {
                  flag=false;
                  break;
               }
            }
            if(flag) {
               m[++num]=BigInteger.valueOf(sz);
               r[num]=BigInteger.valueOf((-j+sz)%sz);
               break;
            }
         }
         if(!flag)
            break;
      }
      BigInteger n2,ans;
      if(flag) {
         n2=Ex_Crt().mod(n1).add(n1).mod(n1);
         n1=n1.multiply(BigInteger.valueOf(2));
         n2=n2.multiply(BigInteger.valueOf(2)).add(BigInteger.ONE);
         ans=n1.min(n2);
      }
      else {
         n1=n1.multiply(BigInteger.valueOf(2));
         ans=n1;
      }
      if(ans.compareTo(new BigInteger("1000000000000"))>0) {
         System.out.println("huge");
      }
      else {
         System.out.println(ans);
      }
      input.close();
   }
}

Problem E: Efficient Exchange

在这里插入图片描述
在这里插入图片描述
dp的思路:
给了一个数字n,我们从高位向低位递推
就比如样例给的83,我们可以先凑出80,也可以先凑出90
dp数组的第二维如果是0表示在当前这一位上我们使得当前的数字等于要凑的数,为1表示凑成要凑的数+1
比如83,dp[1][0]表示凑成80要多少次,dp[1][1]表示凑成90要多少次
然后dp[2][0]就是凑成83需要的次数,就是答案
递推式:
dp[i+1][0]=Math.min(dp[i][0]+s.charAt(i)-‘0’,dp[i][1]+10-s.charAt(i)+‘0’);
dp[i+1][1]=Math.min(dp[i][0]+s.charAt(i)-‘0’+1, dp[i][1]+10-s.charAt(i)+‘0’-1);

import java.math.*;
import java.util.*;
public class Main {
 static String s;
 static int MAXN=10000;
 static int[][] dp=new int[MAXN][2];
 public static void main(String agvs[]) {
  dp[0][0]=0;
  dp[0][1]=1;
  Scanner input=new Scanner(System.in);
  s=input.nextLine();
  for(int i=0;i<s.length();i++) {
   dp[i+1][0]=Math.min(dp[i][0]+s.charAt(i)-'0',dp[i][1]+10-s.charAt(i)+'0');
   dp[i+1][1]=Math.min(dp[i][0]+s.charAt(i)-'0'+1, dp[i][1]+10-s.charAt(i)+'0'-1);
  }
  System.out.println(dp[s.length()][0]);
 }
}

Problem F: Find my Family

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

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=3e5+10;
const ll INF=0x3f3f3f3f;
vector<ll>ans;
ll a[MAXN];
ll MAX[MAXN];
int main(){
    ll k;
    scanf("%lld",&k);
    for(ll i=1;i<=k;i++){
        ll n;
        scanf("%lld",&n);
        for(ll j=1;j<=n;j++)
        scanf("%lld",&a[j]);
        set<ll>se;
        ll M=0;
        for(ll j=n;j>0;j--){
            if(a[j]>M){
                MAX[j]=a[j];
                M=a[j];
            }
            else{
                MAX[j]=M;
            }
        }
        for(ll j=1;j<=n;j++){
            if(se.empty()||MAX[j]==a[j]){
                se.insert(a[j]);
                continue;
            }
            auto pos=se.upper_bound(a[j]);
            if(pos!=se.end()){
                if(*pos<MAX[j]){
                    ans.push_back(i);
                    break;
                }
            }
            se.insert(a[j]);
                    }
    }
    ll sz=ans.size();
    printf("%lld\n",sz);
    for(ll i=0;i<sz;i++){
        printf("%lld\n",ans[i]);
    }
}

Problem G: Gluttonous Goop

在这里插入图片描述
在这里插入图片描述
小范围爆搜,大范围和小范围在最小的包括所有点的矩形中缺失的块数和小范围是一致的

import java.util.*;
import java.math.*;
public class Main {
   static int MAXN=100;
   static int INF=0x3f3f3f3f;
   static boolean[][] vis=new boolean[MAXN][MAXN];
   public static void main(String[] args) {
   Scanner input=new Scanner(System.in);
   int r=input.nextInt();
   int c=input.nextInt();
   int k=input.nextInt();
   int num=k>20?20:k;
   for(int i=1;i<=r;i++) {
      String s=input.next();
      for(int j=0;j<s.length();j++) {
         if(s.charAt(j)=='#') {
            vis[i+30][j+31]=true;
            for(int t=-num;t<=num;t++) {
               for(int y=-num;y<=num;y++) {
                  vis[i+30+t][j+31+y]=true;
               }
            }
         }
      }
   }
   int cnt=0;
   int minr=INF,maxr=0,minc=INF,maxc=0;
   for(int i=31-num;i<=30+r+num;i++) {
      for(int j=31-num;j<=30+c+num;j++) {
         if(vis[i][j]==true) {
            cnt++;
            minr=Math.min(minr, i);
            maxr=Math.max(maxr, i);
            minc=Math.min(minc, j);
            maxc=Math.max(maxc, j);
         }
      }
   }
   if(cnt==0)
   System.out.println(cnt);
   else {
      if(k<=20) {
         System.out.println(cnt);
      }
      else {
         int len=maxr-minr+1;
         //System.out.println(len);
         int wid=maxc-minc+1;
         //System.out.println(wid);
         int size=len*wid;
         int lack=size-cnt;
         //System.out.println(lack);
         BigInteger ans=BigInteger.ONE;
         ans=ans.multiply(BigInteger.valueOf(len+(k-num)*2));
         ans=ans.multiply(BigInteger.valueOf(wid+(k-num)*2));
         ans=ans.subtract(BigInteger.valueOf(lack));
         System.out.println(ans);
      }
   }
   }
}

Problem H: Historic Exhibition

在这里插入图片描述
在这里插入图片描述
贪心即可,网络流会超时

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+10;
int ans[MAXN];
struct node{
    int x,id;
    friend bool operator <(node a,node b){
        return a.x==b.x?a.id<b.id:a.x<b.x;
    }
}va[MAXN];
unordered_map<int,stack<int> >mp1,mp2;
int main(){
    int p,v;
    scanf("%d%d",&p,&v);
    int a,b;
    for(int i=1;i<=p;i++){
        scanf("%d%d",&a,&b);
        if(a==b){
            mp1[a].push(i);
        }
        else{
            mp2[min(a,b)].push(i);
        }
    }
    for(int i=1;i<=v;i++){
        scanf("%d",&a);
        va[i].id=i;
        va[i].x=a;
    }
    sort(va+1,va+1+v);
    for(int i=1;i<=v;i++){
        if(!mp1[va[i].x].empty()){
            ans[va[i].id]=mp1[va[i].x].top();
            mp1[va[i].x].pop();
        }
        else if(!mp2[va[i].x-1].empty()){
            ans[va[i].id]=mp2[va[i].x-1].top();
            mp2[va[i].x-1].pop();
        }
        else if(!mp2[va[i].x].empty()){
            ans[va[i].id]=mp2[va[i].x].top();
            mp2[va[i].x].pop();
        }
        else{
            printf("impossible\n");
            return 0;
        }
    }
    for(int i=1;i<=v;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

Problem I: Inquiry II

在这里插入图片描述
dfs回溯+树形dp求最大独立集

#include<bits/stdc++.h>
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
struct Edge{
    int to;
    int next;
}edge[200],edge2[200];
int cnt,cnt2,n,m;
int ans,sz,curans;
int head[200],dp[200][2],head2[200],visv[200],vise[200],unuse[200];
vector<int>outtree;
void add(int u,int v){
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void add2(int u,int v){
    edge2[cnt2].to=v;
    edge2[cnt2].next=head2[u];
    head2[u]=cnt2++;
}
void build(int u,int fa){
    visv[u]=1;
    for(int i=head[u];~i;i=edge[i].next){
        int v=edge[i].to;
        if(v==fa)continue;
        if(!visv[v]){
            vise[i/2]=1;
            add2(u,v);
            add2(v,u);
            build(v,u);
        }
    }
}
void treedp(int u,int fa){
    if(!unuse[u])
    dp[u][1]=1;
    for(int i=head2[u];~i;i=edge2[i].next){
        int v=edge2[i].to;
        if(v==fa)continue;
        treedp(v,u);
        dp[u][0]+=max(dp[v][1],dp[v][0]);
        dp[u][1]+=dp[v][0];
        //de(dp[v][1]);
        /*if(!unuse[u]){
            if(!unuse[v]){
                dp[u][0]+=max(dp[v][1],dp[v][0]);
                dp[u][1]+=dp[v][0];
                }
            else{
                dp[u][0]+=dp[v][0];
                dp[u][1]+=dp[v][0];
                }
        }
        else{
            if(!unuse[v]){
                dp[u][0]+=max(dp[v][1],dp[v][0]);
                }
            else{
                dp[u][0]+=dp[v][0];
            }
        }*/
    }
}
void dfs(int e){
    if(e==sz){
        memset(dp,0,sizeof(dp));
        /*for(int i=1;i<=n;i++){
                if(unuse[i])
                    cout<<i<<' ';
        }
        cout<<endl;*/
        treedp(1,0);
        /*if(unuse[1])
            curans=dp[1][0];
        else*/
        curans=max(dp[1][0],dp[1][1]);
        //de(curans);
        //cout<<curans<<endl;
        ans=max(ans,curans);
        return ;
    }
    int u=edge[2*outtree[e]].to,v=edge[2*outtree[e]+1].to;
    //de(u);
    //de(v);
    if(!unuse[u])
    unuse[u]=e+1;
    dfs(e+1);
    if(unuse[u]==e+1)
    unuse[u]=0;
    if(!unuse[v])
    unuse[v]=e+1;
    dfs(e+1);
    if(unuse[v]==e+1)
    unuse[v]=0;
}
int main(){
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    int u,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    build(1,0);
    for(int i=0;i<cnt/2;i++){
        if(!vise[i])
        outtree.push_back(i);
    }
    sz=outtree.size();
    //de(sz);
    dfs(0);
    cout<<ans<<endl;
    return 0;
}
/*
10 11
1 2
1 6
6 5
4 5
3 4
3 2
4 9
5 8
9 10
10 7
7 8
*/

Problem J: Jazz it Up!

在这里插入图片描述
签到题
数论

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e5+10;
ll prime[MAXN],cnt;
bool isprime[MAXN];
void init()
{
    cnt=1;
    memset(isprime,1,sizeof(isprime));
    isprime[0]=isprime[1]=0;
    for(ll i=2;i<=MAXN;i++)
    {
        if(isprime[i])
        {
            prime[cnt++]=i;
        }
        for(ll j=i*2;j<=MAXN;j+=i)
        {
            isprime[j]=0;
        }
    }
}
int main(){
    init();
    ll n;
    ll ans;
    scanf("%lld",&n);
    for(ll i=1;i<=cnt;i++){
        if(n%prime[i]){
           ans=prime[i];
           break;
        }
    }
    printf("%lld\n",ans);
}

Problem K: Keep Him Inside

在这里插入图片描述
在这里插入图片描述
构造计算几何
给每一个点一个权重,使得这些点加权和等于p
这个点在多边形内部,找到这个点所在的三角形,然后解方程组 a ∗ x 1 + b ∗ x 2 + ( 1 − a − b ) ∗ x 3 = p x , a ∗ y 1 + b ∗ y 2 + ( 1 − a − b ) ∗ y 3 , a , b , 1 − a − b a*x_1+b*x_2+(1-a-b)*x_3=px,a*y_1+b*y_2+(1-a-b)*y_3,a,b,1-a-b ax1+bx2+1ab)x3=px,ay1+by2+(1ab)y3,a,b,1ab是三角形三个点的答安,其他店的答案为0

#include<bits/stdc++.h>
using namespace std;
struct Point{
    double x, y;
    Point(double x = 0, double y = 0):x(x),y(y){}
}p,no[100];
double ans[100];
typedef Point Vector;
int Cross(Vector A, Vector B){
    return A.x*B.y-A.y*B.x;
}
Vector operator - (Point A, Point B){
    return Vector(A.x-B.x, A.y-B.y);
}
int main(){
    int n;
    scanf("%d%lf%lf",&n,&p.x,&p.y);
    for(int q=1;q<=n;q++){
        scanf("%lf%lf",&no[q].x,&no[q].y);
    }
    int i;
    for(i=3;i<=n;i++){
        if(Cross(no[i]-p,no[1]-no[i])>0)
            break;
    }
    double x1=no[1].x,y1=no[1].y,x2=no[i-1].x,y2=no[i-1].y,x3=no[i].x,y3=no[i].y,px=p.x,py=p.y;
    ans[1]=((x2-x3)*(y3-y2)-(px-x3)*(y3-y2)-(py-y2)*(x2-x3))/((x2-x3)*(y3-y2)-(x1-x3)*(y3-y2)-(y1-y2)*(x2-x3));
    if(x2-x3==0){
    ans[i]=(py-y2-(y1-y2)*ans[1])/(y3-y2);
    ans[i-1]=1-ans[1]-ans[i];
    }
    else{
    ans[i-1]=(px-x3-(x1-x3)*ans[1])/(x2-x3);
    ans[i]=1-ans[i-1]-ans[1];
    }
    for(int k=1;k<=n;k++){
        printf("%.8f\n",ans[k]);
    }
}

Problem L: Lucky Draw

在这里插入图片描述
概率题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e3;
long double c[MAXN+10][MAXN+10];
void init(){
    c[0][0]=1;
    for(int i=1;i<=MAXN;i++){
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;j++)
        {
            c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
    }
}
int main(){
    init();
    int n,k;
    long double p;
    scanf("%d%d",&n,&k);
    scanf("%llf",&p);
    long double cur=0;
    long double tot=0;
    long double ans=1;
    for(int i=1;i<=MAXN;i++){
        if(i>=k){
        long double P=c[i-1][k-1]*pow(p,i-k)*pow(1-p,k);
        tot=tot+P*pow(cur,n-1);
        cur=cur+P;
        }
    }
    ans=1-n*tot;
    printf("%.15Lf\n",ans);
    return 0;
}

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值