1. 程式人生 > >2017HDU多校7場

2017HDU多校7場

題目連結

一些題解

05 Euler theorem

 本場的水題,給定一個a問任意的b讓a % b有幾種可能的值。
 可以得見0 - (a / 2 - 1)都是可以得到的值, 而且其本身也是可以得到的值所以得到程式碼。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int main()
{
    int t;
    scanf("%d", &t);
    while
(t--) { int a; scanf("%d", &a); int ans; if(a % 2 == 0) ans = a / 2 + 1; else ans = (a + 1) / 2 + 1; printf("%d\n", ans); } return 0; }

08 Hard challenge

 題意是說有n個點,每兩個點都不在一條直線上,把每兩個點相連,其邊的權值就是給定的兩個點的值的乘積。現在從原點做一條直線問這條直線所穿過的所有邊的權值的和最大是多少。
 可以把點分作左右兩邊來對待,而左右兩邊點的權值之和就是(A + B + C +…) * (a + b + c + ….)。我們將圖分為上下兩塊,然後把角度大於180°的點角度減去180°,並標記點是上面的點還是下面的點。按角度排序從小到大掃一遍。當掃到的點是上面的點時,sum上 - point.value, sum下 + point.value;當掃到下面的點相反即可。再相乘求其最大值。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define rep(i, s, t) for(int i = s;i <= t;i++)
#define rap(i, s, t) for(int i = s;i >= t;i--)
using namespace std;
typedef long long LL;
const double PI = acos(-1.0);
struct
point { double x, y; double angle; LL value; int flag; }p[50005]; int n; int cmp(point a, point b) { return a.angle < b.angle; } int main() { int t; scanf("%d", &t); while(t--) { LL maxn = 0; LL sumu = 0, sumd = 0; scanf("%d", &n); rep(i, 1, n){ scanf("%lf%lf%I64d", &p[i].x, &p[i].y, &p[i].value); if(p[i].y >= 0) p[i].flag = 1, sumu += p[i].value; else p[i].flag = 0, sumd += p[i].value; double pp = acos(p[i].x / sqrt(p[i].x * p[i].x + p[i].y * p[i].y)); pp = pp * 180 / PI; if(p[i].flag == 0) p[i].angle = 180 - pp; else p[i].angle = pp; } sort(p + 1, p + 1 + n, cmp); rep(i, 1, n){ if(p[i].flag == 1) { sumu -= p[i].value; sumd += p[i].value; } else { sumd -= p[i].value; sumu += p[i].value; } maxn = max(maxn, sumd * sumu); } printf("%I64d\n", maxn); } return 0; }

11 Kolakoski

 題意是求Kolakoski序的第n個值,一開始妄圖使用Kolakoski序的性質即a(a(1) + a(2) + a(3) + a(k)) = (3 + (-1)^k) / 2發現超時且沒必要,直接模擬其定義即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a[10000004];
int main()
{
    a[1] = 1;
    a[2] = 2;
    a[3] = 2;
    int ans = 4;
    int right = 3;
    while(ans <= 10000000)
    {
        if(a[right] == 2&&a[ans - 1] == 2)
            a[ans++] = 1, a[ans++] = 1;
        else if(a[right] == 2&&a[ans - 1] == 1)
            a[ans++] = 2, a[ans++] = 2;
        else if(a[right] == 1)
        {
            if(a[ans - 1] == 2)
                a[ans++] = 1;
            else
                a[ans++] = 2;
        }
        right++;
    }
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        printf("%d\n", a[n]);
    }
    return 0;
}

下面是後來補的題

02 Build a tree

 這題當時寫k叉樹沒搞定網上搜的題解真是巧妙進行滿編,非滿編,滿編卻少一層的標記,然後每次更新狀態,由下至上統計即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        LL n, k;
        LL ans;
        LL s1, s2, s3, n1, n2, n3;
        scanf("%lld%lld", &n, &k);
        ans = 0;
        s1 = n1 = s2 = n2 = s3 = n3 = 0;
        if(k == 1)
        {
            if(n % 4 == 1)
                printf("1\n");
            else if(n % 4 == 2)
                printf("%I64d\n", n + 1);
            else if(n % 4 == 3)
                printf("0\n");
            else if(n % 4 == 0)
                printf("%I64d\n", n);
            continue;
        }
        LL ls = 1;
        while(n > 0)
        {
            n -= ls;
            if(n / ls < k)
                break;
            ls *= k;
        }
        if(n)
        {
            n1 = n;
            s1 = 1;
            if(n&1)
                ans = 1;
        }
        while(ls > 0)
        {
            if(n1 < k)//此時將不會再有單獨的滿員狀態,將n1和n2狀態合併
            {
                s1 = n1 * s1 + s2 + s3 * (k - n1 - n2) + 1;
                n1 = 1;
                n2 = 0;
                s2 = 0;
            }
            else
            {
                if(n1 % k)
                {
                    s2 = n1 % k * s1 + (k - n1 % k - n2) * s3 + s2 + 1;
                    n2 = 1;
                }
                else if(n2)
                {
                    s2 = s2 + (k - 1) * s3 + 1;
                }
                s1 = s1 * k + 1;
                n1 = n1 / k;
            }
            s3 = s3 * k + 1;
            n3 = ls - n1 - n2;
            if(n1 & 1)ans ^= s1;
            if(n2 & 1)ans ^= s2;
            if(n3 & 1)ans ^= s3;
            ls /= k;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

10 Just do it

 這道題的題意是說給定一個ai然後每一個bi都a1^a2^….ai問這樣進行m次後最終得到的數列序列是什麼。
 由異或性質可知一個數的奇數個異或就為本生,偶數個異或為0。在比賽時打表直接簡化為01表了,雖然發現了4進位制的規律但是並沒有別的發現,而且寫法複雜。賽後隊友發現是保留原來的係數發現就是楊輝三角斜著下來。而判斷Cmn是否為奇數的方法是M&N == N?奇數:偶數。而每一個i,m所對應的M就是i + m - 2,N就是i - 1(表中規律),這樣就可以得到所要異或的值。
 之後i從1到n遍歷,但是其所代表的其實是an-a1然後對每個可以到達第i個的數,他們在第i個的係數都是一樣的,所以再遍歷異或即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define rep(i, s, t) for(int i = s;i <= t;i++)
#define rap(i, s, t) for(int i = s;i >= t;i--)
using namespace std;
typedef long long LL;
int n, m;
LL stand[200004];
LL ans[200004];
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        memset(stand, 0, sizeof(stand));
        memset(ans, 0, sizeof(ans));
        scanf("%d%d", &n, &m);
        rep(i, 1, n)
            scanf("%I64d", &stand[i]);
        rep(i, 1, n)
        {
            int nn = m + i - 2;
            int mm = i - 1;
            if((nn & mm) == mm)
                rep(j, i, n)
                    ans[j] ^= stand[j - i + 1];
        }
        rep(i, 1, n)
            printf("%I64d%c", ans[i], i == n?'\n':' ');
    }
    return 0;
}