1. 程式人生 > >洛谷11月月賽

洛谷11月月賽

https://www.luogu.org/contestnew/show/12006

我是比賽完後在去寫的

這是我第一次打洛谷月賽,之前一次是比賽完才去看而且寫了第一題就沒寫後面的了

284分,太水了,rank85左右

第一題第二題AC了,第三題寫了3^n的演算法,知道會超時,也知道滿分做法應該是隻考慮有價值的狀態

但是還是沒想出正解,拿了70分

第四題想到了講評中說的錯誤的做法,然後細節比較多,怒剛1.5h,然而寫掛了,交上去14分……

還不如寫暴力有30~40分

考場策略出錯了,如果最後一道題考慮騙分,能上300(然而300分rank還是很後)

 

P4994 終於結束的起點

5分鐘AC。

簽到題,陣列都不用開。

寫的時候不太確定會不會答案非常大導致超時,但是直接交就AC了

好像有個什麼定理可以得出答案不會太大。反正我ac了就好

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= b; i++)
using namespace std;

int main()
{
    int m;
    scanf(
"%d", &m); int a = 0, b = 1; int ans = 0; while(1) { ans++; int t = (a + b) % m; a = b; b = t; if(a == 0 && b == 1) { printf("%d\n", ans); break; } } return 0; }

 

P4995 跳跳!

30分鐘AC

看完題腦中閃過貪心的念頭,但是後來看到資料範圍400,就去想n^3的dp了

然後最後折騰了一番,才發覺來回跳一定比順著跳要優。

那是不是一直來回跳就是最優的

然後我就猜了最高,最低,次高,次低……

然後就AC了。

大膽猜想!!

#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= b; i++)
using namespace std;

typedef long long ll;
const int MAXN = 300 + 10;
ll dp[MAXN], a[MAXN], ans;
int n;

int main()
{
    scanf("%d", &n);
    _for(i, 1, n) scanf("%lld", &a[i]);
    a[0] = 0;
    sort(a, a + n + 1);
    
    int l = 0, r = n;
    while(l < r)
    {
        ans += (a[r] - a[l]) * (a[r] - a[l]);
        l++;
        if(l >= r) break;
        ans += (a[r] - a[l]) * (a[r] - a[l]);
        r--;
    }
    printf("%lld\n", ans);

    return 0;
}

 

P4996 咕咕咕

1h10min拿了70分

第一反應就是列舉子集,3^n

但是自己沒注意到一些細節,以及沒想清楚當前這個狀態的價值要乘上方案,所以一直調來調去

一個小時左右才過了樣例

#include<bits/stdc++.h>
#define add(a, b) a = (a + b) % mod
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= b; i++)
using namespace std;

typedef long long ll;
const int mod = 998244353;
const int MAXN = (1 << 20) + 10;
int dp[MAXN], a[MAXN], num[MAXN], ans;
char s[MAXN];
int n, m;

int main()
{
    scanf("%d%d", &n, &m);
    _for(i, 1, m)
    {
        int x = 0, y; 
        scanf("%s%d", s, &y);
        REP(i, 0, strlen(s))
            x = x * 2 + s[i] - '0';
        a[x] = y;
    }
    
    dp[0] = a[0];
    num[0] = 1;
    REP(S, 1, 1 << n)
    {
        for(int S0 = (S - 1) & S; ; S0 = (S0 - 1) & S)
        {
            add(dp[S], dp[S0]);
            add(num[S], num[S0]);
            if(!S0) break;
        }
        add(dp[S], 1ll * num[S] * a[S]);
    }
    printf("%d\n", dp[(1 << n) - 1]);

    return 0;
}

顯然會超時

可以只考慮有價值的狀態對答案的貢獻

顯然把包含這個狀態的方案數乘以這個狀態的價值就是對答案的貢獻

那麼考慮怎麼算方案

比如對於011

所有方案都是從000到011再到111

那麼考慮從000到011的方案

顯然是把兩個0變成兩個1的方案

更一般的來說,可以初始化出一個數組,num[i]表示把i個0變成i個1的方案數

考慮把j個0一起變成1

那麼有c(i, j) * num[i-j]種方案

那麼列舉j就可以了

現在想想其實不難,但是考試的時候就是沒想到

做題不夠多

#include<bits/stdc++.h>
#define add(a, b) a = (a + b) % mod
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= b; i++)
using namespace std;

typedef long long ll;
const int mod = 998244353;
const int MAXN = 21;
ll c[MAXN][MAXN], num[MAXN], ans;
char s[MAXN];
int n, m;

void init()
{
    _for(i, 0, 20)
    {
        c[i][0] = 1;
        _for(j, 1, i)
            add(c[i][j], c[i-1][j-1] + c[i-1][j]);
    }
    
    num[0] = 1;
    _for(i, 1, 20)
        _for(j, 1, i)
            add(num[i], c[i][j] * num[i-j]);
}

int main()
{
    init();
    scanf("%d%d", &n, &m);
    _for(i, 1, m)
    {
        int x = 0, a, cnt = 0; 
        scanf("%s%d", s, &a);
        REP(i, 0, strlen(s))
            cnt += (s[i] == '1');
        add(ans, a * num[cnt] % mod * num[n-cnt]); 
    }
    printf("%lld\n", ans);
    return 0;
}