第十一届山东省大学生程序设计竞赛 补题

B.Build Roads

题意: 给定一个 n n n个点的无向完全图, i i i j j j之间的边权是 gcd ⁡ ( a i , a j ) \gcd{(a_i,a_j)} gcd(ai,aj),保证数组 a a a随机,求 MST \text{MST} MST
分析: 如果有点权为素数 p p p ∀ x ∈ R , gcd ⁡ ( p , x ) = 1 \forall x\in R,\gcd{(p,x)}=1 xR,gcd(p,x)=1,所以如果找到素数,就以素数的点为中心,与其他点建立 n − 1 n-1 n1条边,这样 MST \text{MST} MST的最小值为 n − 1 n-1 n1int范围内的素数间距最小间距不到几百,所以在 n ≤ 1000 n \le1000 n1000时直接暴力, n > 1000 n>1000 n>1000时输出 n − 1 n-1 n1
另外,如果 L = R L=R L=R,则输出 L ∗ ( n − 1 ) L*(n-1) L(n1)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=2e5+5;
ULL seed,n,L,R,a[N],res;
ULL xorshift64(){
     
    ULL x=seed;
    x^=x<<13;
    x^=x>>7;
    x^=x<<17;
    return seed=x;
}
int gen(){
     
    return xorshift64()%(R-L+1)+L;
}
int main(){
     
    cin>>n>>L>>R>>seed;
    if(L==R) cout<<L*(n-1)<<endl;
    else if(n<=1000){
     
        vector<ULL> x;
        for(int i=1;i<=n;i++) a[i]=gen();
        for(int i=1;i<n;i++)
            for(int j=i+1;j<=n;j++)
                x.push_back(__gcd(a[i],a[j]));
        sort(x.begin(),x.end());
        for(int i=0;i<n-1;i++) res+=x[i];
        cout<<res<<endl;
    }
    else cout<<n-1<<endl;
}

C.Cat Virus

题意: 给定一棵树,黑白染色方案,满足一个黑点的子树都是黑点,白点任意。你现在构造一棵树,使得它的染色方案数为 K K K
分析: 首先一个节点必定有全黑的方案,所以就可以转移到子树节点的方案数,如果 K K K为奇数,那么这个节点连接一个左儿子,一个右儿子,然后 K / 2 K/2 K/2。如果 K K K为偶数,直接连一个右儿子,使 K − 1 K-1 K1变为奇数。注意特判 K = 3 K=3 K=3的情况。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL k;
int main(){
     
    cin>>k;
    LL fa=1,id=1,tmp=k,cnt=1;
    while(tmp>3){
     
        if(tmp&1){
     
            tmp/=2;
            cnt+=2;
        }
        else{
     
            tmp--;
            cnt++;
        }
    }
    if(tmp==3) cnt++;
    cout<<cnt<<endl;
    while(k>3){
      
        if(k&1){
     
            k/=2;
            id++;
            cout<<fa<<" "<<id<<endl;
            id++;
            cout<<fa<<" "<<id<<endl;
            fa=id;
        }
        else{
     
            k--;
            id++;
            cout<<fa<<" "<<id<<endl;
            fa=id;
        }
    }
    if(k==3){
     
        id++;
        cout<<fa<<" "<<id<<endl;
    }
}

D.Dyson Box

题意: 二维空间里放了个 n n n盒子,有水平往左和竖直往下两种重力,求重力作用之后形成的轮廓周长。
分析: 模拟。每次放的方块对答案贡献是 4 4 4,如果这行(列)被放过方块,那么答案 − 2 -2 2,如果移动之后相邻的方块被放置过,那么答案也分别 − 2 -2 2,只需要记录每次的高度再判断一下相邻就可以。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long LL;
LL n,x,y,res1,sx1[N],sy1[N],res2,sx2[N],sy2[N];
int main(){
     
    cin>>n;
    for(int i=1;i<=n;i++){
     
        cin>>x>>y;
        res1+=4;
        if(sx1[x]) res1-=2;
        else sx1[x]=1;
        sy1[x]++;
        if(sy1[x-1]>=sy1[x]) res1-=2;
        if(sy1[x+1]>=sy1[x]) res1-=2;
        res2+=4;
        if(sy2[y]) res2-=2;
        else sy2[y]=1;
        sx2[y]++;
        if(sx2[y-1]>=sx2[y]) res2-=2;
        if(sx2[y+1]>=sx2[y]) res2-=2;
        cout<<res1<<" "<<res2<<endl;
    }
}

G.Grade Point Average

题意: 输出 ∑ i = 1 n a i n \frac{\sum^{n}_{i=1}a_i}{n} ni=1nai,保留小数点后 k k k位(直接截断后面的)
分析: 模拟除法。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,k,x,sum;
int main(){
     
    cin>>n>>k;
    for(int i=1;i<=n;i++){
     
        cin>>x;
        sum+=x;
    }
    cout<<sum/n<<".";
    sum%=n;
    while(k--){
     
        cout<<sum*10/n;
        sum*=10;
        sum%=n;
    }
}

H.Adventurer’s Guild

题面: Yuna \text{Yuna} Yuna 的生命值是 H H H,体力值是 S S S,有 n n n个任务,每个任务有生命值和体力值的花费。生命值不能降到 0 0 0 0 0 0以下,体力值的多余消耗会算到生命值里。求最大任务收益。

分析: 二维费用背包板子题,状态转移方程:
{ d p [ j ] [ k ] = m a x ( d p [ j ] [ k ] , d p [ j − h [ i ] + k − s [ i ] ] [ 0 ] + w [ i ] ) , if  k − s [ i ] < 0 a n d j − h [ i ] + k − s [ i ] ≤ 0 d p [ j ] [ k ] = m a x ( d p [ j ] [ k ] , d p [ j − h [ i ] ] [ k − s [ i ] ] + w [ i ] ) , else \begin{cases} dp[j][k]=max(dp[j][k],dp[j-h[i]+k-s[i]][0]+w[i]),\text{if }k-s[i]<0&and&j-h[i]+k-s[i]\le 0\\ dp[j][k]=max(dp[j][k],dp[j-h[i]][k-s[i]]+w[i]),\text{else} \end{cases} { dp[j][k]=max(dp[j][k],dp[jh[i]+ks[i]][0]+w[i]),if ks[i]<0dp[j][k]=max(dp[j][k],dp[jh[i]][ks[i]]+w[i]),elseandjh[i]+ks[i]0

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1010,M=305;
LL n,H,S,h[N],s[N],w[N],dp[M][M];
int main(){
     
    cin>>n>>H>>S;
    for(int i=1;i<=n;i++) cin>>h[i]>>s[i]>>w[i];
    for(int i=1;i<=n;i++){
     
        for(int j=H;j>h[i];j--){
     
            for(int k=S;k>=0;k--){
     
                if(k-s[i]<0){
     
                    if(j-h[i]+k-s[i]<=0) continue;
                    dp[j][k]=max(dp[j][k],dp[j-h[i]+k-s[i]][0]+w[i]);
                }
                else{
     
                    dp[j][k]=max(dp[j][k],dp[j-h[i]][k-s[i]]+w[i]);
                }
            }
        }
    }
    cout<<dp[H][S]<<endl;
}

M.Matrix Problem

题意: 给定 0 / 1 0/1 0/1矩阵 C C C,构造两个矩阵 A , B A,B A,B,其中 1 1 1形成了完整的不分散的一块四连通块,并且对于 C C C中所有位置,若是 1 1 1,则 A , B A,B A,B对应位置必须都是 1 1 1,否则 A , B A,B A,B之中必须有一个这个位置为 0 0 0。保证 C C C阵的边框都是 0 0 0
分析: 思维题。 A A A最左边一列是 1 1 1 B B B最右边一列是 1 1 1,然后行分奇偶全染成 1 1 1

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=505;
char c[N][N],a[N][N],b[N][N];
int n,m;
int main(){
     
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>c[i];
    for(int i=0;i<n;i++){
     
        for(int j=0;j<m;j++){
     
            if(c[i][j]=='1') a[i][j]='1';
            else a[i][j]='0';
            if(i%2==0&&j<m-1) a[i][j]='1';
            if(!j) a[i][j]='1';
        }
    }
    for(int i=0;i<n;i++){
     
        for(int j=0;j<m;j++){
     
            if(c[i][j]=='1') b[i][j]='1';
            else b[i][j]='0';
            if(i%2==1&&j) b[i][j]='1';
            if(j==m-1) b[i][j]='1';
        }
    }
    for(int i=0;i<n;i++) cout<<a[i]<<endl;
    for(int i=0;i<n;i++) cout<<b[i]<<endl;
}

你可能感兴趣的:(算法)