2016计科学院ACM院赛题解

前言

虽然忙碌了几周,但是总算院赛完美落幕了。比赛中没出现大的差错,哈哈!!

题解

  • A.变态的青蛙

    设:dp[i]为跳上i级台阶总共的跳法。
    那么dp[i] = dp[1] + dp[2] + ... + dp[i-1] + 1
    那么很容易推出dp[i]的规律:
    

    dp[i]=2i1(dp[0]=0)

    又因为题目n很大,所以要使用快速幂!
    

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
ll n;

ll p(ll a, ll b)
{
    if(b == 0) return 1;
    if(b%2) return a*p(a*a%mod,b/2)%mod;
    return p(a*a%mod,b/2);
}

int main()
{
    while(scanf("%lld",&n)!=EOF)
    {
        if(n == 0) printf("0\n");
        else
            printf("%lld\n",p(2,n-1));
    }
    return 0;
}

  • B.最大的数

    并查集处理交换语句,最后可以得到若干个块。
    这样,每个块内元素都可以互相交换。
    那么利用贪心策略:让每个块内元素降序排列即可。
    

代码:

#include <bits/stdc++.h>
using namespace std;
int f[100010];
int n,m;
char str[100010];
vector<int> e[100010];
int pos[100010];
void init()
{
    for(int i = 1 ; i <= n ; i ++)
        e[i].clear();
    for(int i = 1 ; i <= n ; i ++)
        f[i] = i;
    memset(pos,0,sizeof(pos));
}
int fin(int x)
{
    return f[x] == x ? x : f[x] = fin(f[x]);
}
bool cmp(int a,int b)
{
    return a>b;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        scanf("%s",str);
        init();
        int u,v;
        for(int i = 0 ; i < m ; i ++)
        {
            scanf("%d%d",&u,&v);
            int x = fin(u);
            int y = fin(v);
            if(x != y) f[x] = y;
        }
        for(int i =  1; i <= n ; i ++)
        {
            int x = fin(i);
            e[x].push_back(str[i-1]-'0');
        }
        for(int i = 1 ; i <= n ; i ++)
            if(e[i].size()) 
                sort(e[i].begin(),e[i].end(),cmp);
        for(int i = 1 ; i <= n ; i ++)
        {
            int x = fin(i);
            printf("%d",e[x][pos[x]]);
            pos[x] ++;
        }
        printf("\n");
    }
    return 0;
}

  • C.小乐的军事游戏

    补出三角形的外接圆,算出三个顶点和圆心连线的夹角。
    注意:因为存在钝角的缘故,所以第三个角要用2π减去前面两个角来算。
    最后求,三个夹角的最大公约数就行了。
    题目中边数不会超过100,精度eps = 1e-2即可。
    

代码:

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

#define PI acos(-1)
#define eps 1e-2

double fgcd(double a,double b)
{
    return fabs(a) < eps? b : fgcd(fmod(b,a),a);
}

int main() {
    double x1,y1,x2,y2,x3,y3;
    double p,r,s,k,a,b,c;
    double A,B,C;
    while(scanf("%lf %lf %lf %lf %lf %lf", &x1, &y1, &x2, &y2, &x3, &y3)!=EOF)
    {
       a = sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) ) ;
        b = sqrt( (x2-x3)*(x2-x3) + (y2-y3)*(y2-y3) ) ;
        c = sqrt( (x1-x3)*(x1-x3) + (y1-y3)*(y1-y3) ) ;
        p = ( a + b + c ) / 2.0 ;
        s = sqrt( p * (p-a) * (p-b) * (p-c) ) ;
        r = a * b * c / ( 4 * s ) ;


        if( a > c )
        {
            k = a ; a = c ; c = k ;
        }
        if( b > c )
        {
            k = b ; b = c ; c = k ;
        }
        A = 2 * asin(a/(2*r)) ;
        B = 2 * asin(b/(2*r)) ;
        C = 2 * PI - A - B ;

        p = fgcd(A, B);
        p = fgcd(p, C);
        printf("%d\n", (int)(2*PI/p) ) ;

    }
    return 0;
}

  • D.紧急救援

    1~n号城市的距离可以直接跑一遍以1号为源点的Dijstra即可。
    2~n号城市的距离只需要反向建边,然后再跑一遍以1为源点的Dijstra即可。
    

代码:

#include <bits/stdc++.h>
using namespace std;
int mpt1[1010][1010];
int mpt2[1010][1010];
int d1[1010];
int d2[1010];
int n,m;
void init()
{
    memset(mpt1,0x3f3f3f3f,sizeof(mpt1));
    memset(mpt2,0x3f3f3f3f,sizeof(mpt2));
    for(int i = 1 ; i <= n ; i ++)
        mpt1[i][i] = mpt2[i][i] = 0;
}
void dij1()
{
    int vis[1010] = {0};
    memset(d1,0x3f3f3f3f,sizeof(d1));
    d1[1] = 0;
    for(int i = 1 ; i <= n ; i ++)
    {
        int minv = 0x3f3f3f3f,minp = -1;
        for(int j = 1 ; j <= n ;j ++)
        {
            if(vis[j]) continue;
            if(d1[j] < minv)
            {
                minv = d1[j];
                minp = j;
            }
        }
        if(minp == -1) break;
        vis[minp] = 1;
        for(int j = 1 ; j <= n ;j ++)
        {
            if(vis[j])continue;
            if(minv + mpt1[minp][j] < d1[j] ) d1[j] = minv + mpt1[minp][j];
        }
    }
}
void dij2()
{
    int vis[1010] = {0};
    memset(d2,0x3f3f3f3f,sizeof(d2));
    d2[1] = 0;
    for(int i = 1 ; i <= n ; i ++)
    {
        int minv = 0x3f3f3f3f,minp = -1;
        for(int j = 1 ; j <= n ;j ++)
        {
            if(vis[j]) continue;
            if(d2[j] < minv)
            {
                minv = d2[j];
                minp = j;
            }
        }
        if(minp == -1) break;
        vis[minp] = 1;
        for(int j = 1 ; j <= n ;j ++)
        {
            if(vis[j])continue;
            if(minv + mpt2[minp][j] < d2[j] ) d2[j] = minv + mpt2[minp][j];
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i = 0 ; i < m ; i ++)
        {
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);
            if(mpt1[u][v] > c) mpt1[u][v] = c;
            if(mpt2[v][u] > c) mpt2[v][u] = c;
        }
        dij1();dij2();
        int sum = 0 ;
        for(int i = 1 ; i <= n ; i ++)
            sum += d1[i]+d2[i];
        printf("%d\n",sum);
    }
    return 0;
}

  • E.寻宝高手

    首先一趟bfs就可以处理出海域上所有大陆的大小,并且对不同的大陆进行标号。
    然后枚举所有海水点,对于每个海水点O(1)可以得到连接形成陆地的大小。
    然后计数更新即可。
    

代码:

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

int mpt[110][110];
int n,m;
int s[100010];
int siz[110][110];
int vis[110][110];
int ans[110][110];
int dir[4][2] = {1,0,0,1,-1,0,0,-1};
int maxV,maxC,maxX,maxY;
void dfs(int x,int y,int index)
{
    for(int i = 0 ; i < 4 ; i ++)
    {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if(tx < 0 || tx >= n || ty < 0 || ty >= m || vis[tx][ty] || mpt[tx][ty] == 0) continue;
        vis[tx][ty] = 1;
        mpt[tx][ty] = index;
        dfs(tx,ty,index);
    }
}
struct node
{
    int type,value;
    node(int t = 0,int v = 0):type(t),value(v){}
};
bool cmp(node a,node b)
{
    return a.value > b.value;
}
void check(int x,int y)
{
    vector<node> vec;
    for(int i = 0 ; i < 4 ; i ++)
    {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if(tx < 0 || tx >= n || ty < 0 || ty >= m ||  mpt[tx][ty] == 0)continue;
        vec.push_back(node(mpt[tx][ty],siz[tx][ty]));
    }
    int sum = 0;
    vector<int> type;
    for(int i = 0 ; i < vec.size() ; i ++)
    {
        bool flag = true;
        for(int j = 0 ; j < type.size() ; j ++)
        {
            if(vec[i].type == type[j]) flag = false;
        }
        if(flag)
        {
            sum += vec[i].value;
            type.push_back(vec[i].type);
        }
    }
    ans[x][y] = sum;
    if(ans[x][y] > maxV)
    {
        maxC = 1;
        maxV = ans[x][y];
        maxX = x;
        maxY = y;
    }
    else if(ans[x][y] == maxV)
    {
        maxC ++;
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i = 0 ; i < n ; i ++)
        {
            for(int j = 0 ; j < m ; j ++)
            {
                scanf("%d",&mpt[i][j]);
            }
        }
        int index = 2;
        for(int i = 0 ; i < n ; i ++)
        {
            for(int j = 0 ; j < m ; j ++)
            {
                if(mpt[i][j] == 1)
                {
                    memset(vis,0,sizeof(vis));
                    vis[i][j] = 1;
                    mpt[i][j] = index;
                    dfs(i,j,index);
                    index ++;
                }
            }
        }
        memset(s,0,sizeof(s));
        for(int i = 0 ; i < n ; i ++)
        {
            for(int j = 0 ; j < m ; j ++)
            {
                if(mpt[i][j] != 0)
                {
                    ++s[mpt[i][j]];
                }
            }
        }
        for(int i = 0 ; i < n ; i ++)
        {
            for(int j = 0 ; j < m ; j ++)
            {
                if(mpt[i][j] != 0)
                {
                    siz[i][j] = s[mpt[i][j]];
                }
            }
        }
        maxV = -1;
        maxC = 0;
        memset(ans,0,sizeof(ans));
        for(int i = 0 ; i < n ; i ++)
        {
            for(int j = 0 ; j < m ; j ++)
            {
                if(mpt[i][j] == 0)
                    check(i,j);
            }
        }
        if(maxC == 1) printf("%d %d\n",maxX+1,maxY+1);
        else printf("No\n");
    }
    return 0;
}

  • F.XX-XY

    因为长度1-3的时候我们很容易得打答案。
    我们考虑长度n>=4的情况:
    我们将字符串结尾的三个字符状态分为5类:
    

    0:XYZdp[i][0]=dp[i1][0]+2dp[i1][1]+2dp[i1][2]
    1XXYdp[i][1]=3dp[i1][3]+3dp[i1][4]
    2XYXdp[i][2]=dp[i1][0]+dp[i1][1]+dp[i1][2]
    3YXXdp[i][3]=dp[i1][0]+dp[i1][1]+dp[i1][2]
    4XXXdp[i][4]=dp[i1][3]+dp[i1][4]

    因为所求长度n过大,不能直接递推,所以要使用快速矩阵幂。
    

这里写图片描述

代码:

#include <bits/stdc++.h>
using namespace std;
const int mod = 5120141035;
const int MAX = 6;
typedef  struct{
        long long  m[MAX][MAX];
}  Matrix;
Matrix P = {1,0,1,1,0,1,
            2,0,1,1,0,1,
            2,0,1,1,0,1,
            0,3,0,0,1,1,
            0,3,0,0,1,1,
            0,0,0,0,0,0
           };
Matrix I = {1,0,0,0,0,0,
            0,1,0,0,0,0,
            0,0,1,0,0,0,
            0,0,0,1,0,0,
            0,0,0,0,1,0,
            0,0,0,0,0,1
           };
Matrix matrixmul(Matrix a,Matrix b) //¾ØÕó³Ë·¨
{
       int i,j,k;
       Matrix c;
       for (i = 0 ; i < MAX; i++)
           for (j = 0; j < MAX;j++)
             {
                 c.m[i][j] = 0;
                 for (k = 0; k < MAX; k++)
                     c.m[i][j] += (a.m[i][k] * b.m[k][j])%mod;
                 c.m[i][j] %= mod;
             }
       return c;
}
Matrix quickpow(long long n)
{
       Matrix m = P, b = I;
       while (n >= 1)
       {
             if (n & 1)
                b = matrixmul(b,m);
             n = n >> 1;
             m = matrixmul(m,m);
       }
       return b;
}
int main()
{
    int now = clock();
    long long n;
    while(scanf("%lld",&n)!=EOF)
    {
        if(n == 1) printf("4\n");
        else if(n == 2) printf("16\n");
        else if(n == 3) printf("64\n");
        else{
            Matrix mt = quickpow(n-2);
            printf("%lld\n",(mt.m[0][5]*24+mt.m[1][5]*12+mt.m[2][5]*12+mt.m[3][5]*12+mt.m[4][5]*4)%mod);
        }
    }
    return 0;
}

  • G.进程调度

    前置任务   使用  拓扑排序   处理
    优先级     使用  优先队列   处理
    
    值得注意的是:
           这里因为要有限考虑优先度最高的,所以顺向优先队列的选择是局部的。
           所以这要反向建边,然后优先度最低的先出,然后反向输出。
    

代码:

#include <bits/stdc++.h>
using namespace std;
int pre[200010];
vector<int> aft[200010];
int n,m;
int ans[200010],top = 0;
priority_queue<int> team;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(pre,0,sizeof(pre));
        top = 0;
        while(team.size())team.pop();
        for(int i = 0 ; i <= n ; i ++) aft[i].clear();
        for(int i = 0 ; i < m ; i ++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            pre[a] ++;
            aft[b].push_back(a);
        }
        for(int i = 1 ; i <= n ; i ++)
            if(pre[i] == 0) team.push(i);
        while(team.size())
        {
            int now = team.top();
            team.pop();
            ans[top++] = now;
            for(int i = 0 ; i < aft[now].size() ; i ++)
            {
                pre[aft[now][i]] --;
                if(pre[aft[now][i]] == 0) team.push(aft[now][i]);
            }
        }
        for(int i = top - 1 ; i >= 1 ; i --)
        {
            printf("%d ",ans[i]);
        }
        printf("%d\n",ans[0]);
    }
    return 0;
}

  • H.LOL

这里写图片描述

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;

ll inv(ll a)
{
    if(a == 1) return 1;
    return inv(mod%a)*(mod-mod/a)%mod;
}
ll pre[2000010]={0,1};

ll C(ll a,ll b)
{
    if(a == b) return 1;
    if(a == 0) return 1;
    return pre[b]*inv(pre[a])%mod*inv(pre[b-a])%mod;
}

void init()
{
    for(int i = 2 ; i < 2000010 ; i ++)
        pre[i] = (i*pre[i-1])%mod;
}
int main()
{
    ll n,m,k;
    init();
    while(scanf("%lld %lld %lld",&n,&m,&k)!=EOF)
    {
        if(k == 0)
        {
            printf("0\n");
            continue;
        }
        ll step = C(k-1,m+n-1);
        step = step*n%mod;
        step = step*((n*k-k+m)%mod)%mod;
        step = step*inv(m+n-1)%mod;
        printf("%lld\n",step);
    }
    return 0;
}
  • I.A Simple Problem

    题意就是:给你两个数n、m,从小到大求第m个与n互质的数。
    
    做法:
        二分答案,那么这个题目就变成了求[1,x]中与n互质的数的个数。
        筛选出n的质因子个数,然后用容斥定理可以得到。
    

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int fac[20];
int book[10000];
int tot;
ll Count(ll n, int m) {
    ll g = 0, sum = n;
    book[++g] = 1;
    for(int i = 1; i <= tot; i ++){
        int  t = g;
        for(int j = 1; j <= g; ++j){
            book[++t] = book[j] * fac[i] * -1;
            sum += n / book[t];
        }
        g = t;
    }
    return sum;
}
ll solve(int n, int m){
    ll l = 1, r = 10000000000, mid;
    while(l <= r){
        mid = (l + r) >> 1;
        if(Count(mid, n) >= m) r = mid - 1;
        else l = mid + 1;
    }
    return l;
}
void init(int n) {
    int k = 0;
    for(int i = 2; i * i <= n; ++i){
        if(n % i == 0) fac[++k] = i;
        while(n % i == 0) n /= i;
    }
    if(n > 1) fac[++k] = n;
    tot = k;
}
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        init(n);
        ll ans = solve(n, m);
        printf("%lld\n", ans);
    }
    return 0;
}

  • J.字符串

    签到题,开个数组标记一下,遇到出现过的就不输出。
    

代码:

#include <bits/stdc++.h>
using namespace std;
char a[100010];
char b[100010];
int zz[1000];
int main()
{
    while(scanf("%s%s",a,b)!=EOF)
    {
        int na = strlen(a);
        int nb = strlen(b);
        memset(zz,0,sizeof(zz));
        for(int i = 0 ; i < na ; i ++)
           if(zz[a[i]] == 0)
                {
                    printf("%c",a[i]);
                    zz[a[i]]++;
                }
        for(int i = 0 ; i < nb ; i ++)
           if(zz[b[i]] == 0)
                {
                    printf("%c",b[i]);
                    zz[b[i]]++;
                }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值