1. 程式人生 > >2017廣東工業大學ACM新生杯初賽

2017廣東工業大學ACM新生杯初賽

真想再體驗一把新生杯。(部分題解)

Problem A: Chinese Remainder Theorem

思路:中國剩餘定理,套個板子就行了,注意輸入的資料要去重。
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 20;
int n;
LL m[maxn], r[maxn];
int vis[20][20];
LL extend_Euclid(LL a, LL b, LL &x, LL &y)//擴充套件歐幾里得。
{
    if(b==0)
    {
        x = 1; y = 0;
        return a;
    }
    LL r = extend_Euclid(b, a%b, y, x);
    y -= a/b*x;
    return r;
}
LL China()
{
    LL M = 1, ans = 0;
    for (int i = 0; i < n; ++i) M *= m[i];
    for(int i = 0;i < n;i++)
    {
        LL N = M/m[i];
        LL x, y;
        extend_Euclid(N, m[i], x, y);
        x = (x%m[i] + m[i]) % m[i];
        ans = ((ans+r[i]*N%M*x%M)%M + M) % M ;
    }
    return (M+ans%M)%M;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int i,cnt=0;
        memset(vis, 0, sizeof(vis));
        for(i=0; i<n; ++i)
        {
            LL tx, ty;
            scanf("%lld%lld",&tx,&ty);
            if(!vis[tx][ty] == 1)
            {
                vis[tx][ty] = 1;
                r[cnt] = tx;
                m[cnt++] = ty;
            }
        }
        n = cnt;
        LL ans = China(), f=1;
        for(int i=0; i<n; ++i)
        {
            if(ans%m[i] != r[i])
            {
                f = 0;
                puts("IMPOSSIBLE");
                break;
            }
        }
        if(f) printf("%lld\n",ans);
    }
    return 0;
}


Problem B: AC的概率

思路:根據同餘定理,(a+b)%6==0,有(a%6+b%6)%6 == 0,將每個數先取模6,同時記錄到陣列b上,掃一遍即可。
# include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+30;
int a[maxn];
int main()
{
    int t, n;
    scanf("%d",&t);
    while(t--)
    {
        int b[7]={0};
        scanf("%d",&n);
        double tot = (double)n*(n-1)/2, sum=0;
        for(int i=0; i<n; ++i)
        {
            scanf("%d",&a[i]);
            a[i] %= 6;
            if(a[i] == 0) sum += b[a[i]];
            else sum += b[6-a[i]];
            ++b[a[i]];
        }
        printf("%.6f\n",sum*1.0/tot);
    }
    return 0;
}

Problem D: 好難啊

思路:目測是CF Div2的一道題,先不考慮最後一行和最後一列,任意填充前(n-1)*(m-1)個格,那麼最後一行和最後一列顯然可以填上1或-1使得每行每列都等於k,那麼右下角那個格子呢?設前(n-1)*(m-1)個格乘積為sum,對於列來說,右下角填上A=k^(n-1)*sum,對於行來說右下角填上B=k^(m-1)*sum。那麼顯然如果k=-1,且n和m奇偶性不同時A不等於B,其餘情況都能滿足。
# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
typedef long long LL;
int main()
{
    LL n, m, k;
    while(~scanf("%lld%lld%lld",&n,&m,&k))
    {
        if((n&1) != (m&1) && k == -1)
            puts("Orz");
        else puts("O(^_^)O~~");
    }
    return 0;
}

Problem E: 工會首領yjl

思路:貪心,先按Q值排序,所有人得到ceil(Q/2)元,剩下的錢倒著派發,Q值大的先派發。
# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
typedef long long LL;
struct node
{
    int x, id;
}a[103];
int ans[103];
bool cmp(node a, node b)
{
    return a.x < b.x;
}
int main()
{
    int n, m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<n; ++i)
        {
            scanf("%d",&a[i].x);
            a[i].id = i;
        }
        sort(a, a+n, cmp);
        int sum = 0, sum2=0;
        for(int i=0; i<n; ++i)
        {
            int tmp = (int)ceil(a[i].x/2.0);
            ans[a[i].id] = tmp;
            sum += tmp;
            sum2 += a[i].x;
        }
        if(sum > m || sum2 < m)
        {
            puts("gg");
            continue;
        }
        int rest = m-sum;
        for(int i=n-1; i>=0; --i)
        {
            int id = a[i].id;
            int imin = min(rest, a[i].x-ans[id]);
            if(i < n-1) imin = min(imin, ans[a[i+1].id]-ans[id]);
            ans[id] += imin;
            rest -= imin;
        }
        for(int i=0; i<n; ++i)
            printf("%d ",ans[i]);
        puts("");
    }
    return 0;
}

Problem F: 位數是多少?

思路:計算一個數x的位數是log10(x)+1,計算log10(a^b)+1可以化為b*log10(a)+1。
# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
typedef long long LL;
using namespace std;
int main()
{
    int x, y, t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",(int)(y*log10(x))+1);
    }
    return 0;
}

Problem G: 一道簡單的題目


思路:就是直接做,但是資料我故意加了一堆1進去...所以預處理每個1右邊的首個非1數的位置即可,不然會超時。

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int maxn = 1e5+30;
int a[maxn], r[maxn];
int main()
{
    int n, q, L, R, K;
    while(~scanf("%d%d",&n,&q))
    {
        memset(r, 0, sizeof(r));
        memset(a, 0, sizeof(a));
        for(int i=1; i<=n; ++i) scanf("%d",&a[i]);
        r[n] = n+1;
        for(int i=n-1; i>=1; --i)
        {
            if(a[i+1] == 1) r[i] = r[i+1];
            else r[i] = i+1;
        }
        while(q--)
        {
            scanf("%d%d%d",&L,&R,&K);
            for(int i=L; i<=R; i=r[i])
            {
                K /= a[i];
                if(K==0) break;
            }
            printf("%d\n",K);
        }
    }
    return 0;
}

Problem H: 一棵二叉樹

思路:子節點除以二就是父節點的編號,一直往上除直到一存到陣列。比較兩個點的公共祖先最大那個。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;

LL arr[500];

int main()
{
    int T;
    LL a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&a,&b);
        memset(arr,0,sizeof(arr));
        int tmp=0;
        while(a){
            arr[tmp++]=a;
            a>>=1;
        }
        while(b){
            arr[tmp++]=b;
            b>>=1;
        }
        sort(arr,arr+tmp);
        for(int i=tmp-1;i;--i)
            if(arr[i]==arr[i-1]){
                printf("%lld\n",arr[i]);
                break;
            }
    }
    return 0;
}

後面的先留坑