2019年3月考試總結

2019.3.1

T1

題意

一個長度爲\(m\)的環,環上有\(m\)個等距離的點,標號爲0 ... M - 1。環上有\(n\)個線段\((a_i, b_i)\),意義爲從點\(a_i\)順時針延伸到\(b_i\)的線段。
求最多能選出多少個不相交的線段,其中每條線段的端點允許重合。

題解

如果不是環就很好搞了,可以貪心的做。每次選不與當前線段相交的線段中,右端點最靠左的線段就可以保證最優了。也就是下一條選擇的線段的左端點要大於等於上一條選擇線段的右端點。
那麼有環怎麼辦?
還是考慮貪心。
由上面的貪心策略我們可以知道,當我們選擇了一條線段\(i\)後,下一條會選擇的線段是確定的。因此我們令\(Next[i]\)表示選擇了第\(i\)條線段後,下一條會選擇哪條線段。
然後我們拆環成鏈,枚舉每一條線段作爲第一條選擇的線段,那麼最後一條選擇的線段右端點肯定要小於等於枚舉線段的左端點 + m。
因此我們所要求的就變成了,從1條線段出發,走若干步,在到達地點小於某個值的情況下,求最多能走多少步。
於是用倍增處理一下\(Next\)數組即可。

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 201000//因爲把邊複製了,所以要開2倍空間

int n, m, ans;
int f[AC][19], Next[AC], bits[100];
struct road {int l, r;}way[AC];

inline int read()
{
    int x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x;
}

inline void upmin(int &a, int b) {if(b < a) a = b;}
inline void upmax(int &a, int b) {if(b > a) a = b;}
inline bool cmp(road a, road b) {return a.r < b.r;}

void pre()
{
    m = read(), n = read();
    for(R i = 1; i <= n; i ++) 
    {
        way[i].l = read(), way[i].r = read();
        if(way[i].l <= way[i].r) 
            way[n + i].l = way[i].l + m, way[n + i].r = way[i].r + m;
        else way[i].r += m, way[n + i].l = way[i].l + m, way[n + i].r = way[i].r + m;
    }
    n <<= 1, bits[0] = 1;//所以線段數量就翻倍了
    for(R i = 1; i <= 20; i ++) bits[i] = bits[i - 1] * 2;
}

void build()//先找到每條線段下一條是哪一條,然後建出倍增數組
{
    sort(way + 1, way + n + 1, cmp);
    for(R i = 1, j = 1; i <= n; i ++)
    {
        while(way[j].l < way[i].r && j <= n) ++ j;
        if(way[j].l >= way[i].r) f[i][0] = j;//允許端點重合,所以這裏是>=
    }
    /*int tmp = 1, cnt = 0;
    for(R i = 1; i <= n; i ++)
    {
        if(tmp << 1 == i) tmp <<= 1, ++ cnt;
        p[i] = tmp, t[i] = cnt;
    }*/
    for(R i = 1; i <= 18; i ++)
        for(R j = 1; j <= n; j ++) 
            f[j][i] = f[f[j][i - 1]][i - 1];
}

void work()
{
    for(R i = 1; i <= n; i ++)//枚舉每一條線段作爲開頭
    {
        int rnt = 1, now = i, l = way[i].l, r = l + m;//獲取當前枚舉的這個區間 
        for(R j = 18; j >= 0; j --)
        {
            if(!f[now][j]) continue;//特判掉沒有可以選擇的 
            if(way[f[now][j]].r <= r) now = f[now][j], rnt += bits[j]; 
        }
        upmax(ans, rnt);
    }
    printf("%d\n", ans);
}

int main()
{
    freopen("in.in", "r", stdin);
    pre();
    build();
    work();
    fclose(stdin);
    return 0;
}

T2

題意

有一棵\(n\)個節點的樹,有\(k\)個人,每個人選擇一條路徑。一種方案合法當且僅當不存在一條邊恰好被\([2, k - 1]\)條路徑經過。求方案數。

題解

生成函數 + 分治FFT。然而我並不會,也許以後再填坑?

T3

題意

\[f(n) = \begin{cases} 1 \quad n = 1 \\ f(n - f(f(n - 1)) + 1 \quad n > 1 \end{cases}\]
\[g(n) = \sum_{i = 1}^nf(i)\]
\[h(n) = h(g(f(n))) - f(f(n)) + g(g(n))\]
\(T\)組數據,每組給定一個\(n\).求\(h(n)\)
\(T \le 5, n \le 1e9\)

題解

看上去就是一道數論神題。。。
然而……打表神題。
首先對\(f(i)\)打表,大概長這樣:
1 2 2 3 3 4 4 4 5 5 5 6 6 6 6.......
可以發現$f(i) = \(序列中\)i\(的出現次數。(此處的序列指\)f\(數組無限延伸下去構成的序列) 假設已知前2項的值,同時知道這是一個單調不降的序列,那麼可以構造出這個序列。 然後暴力計算出\)f$的前\(1e6\)項。然後發現它至少已經確定了\(f\)的前\(1e9\)項了。
然後分析\(h\)中各部分的含義。
\(f(f(n))\)表示序列中爲\(f(n)\)的出現次數.
\(g(f(n))\)表示序列中最大的,等於\(f(n)\)的位置
那麼\(g(f(n)) - f(f(n))\)則表示序列中第一個等於\(f(n)\)的位置的前一個位置,即最後一個爲\(f(n) - 1\)的位置,即\(g(f(n) - 1)\)
~~ 貌似看到這裏就看不懂了誒~~
所以\(h(n)\)是之前那一段段連續的數的結尾位置的\(g(g(n))\)之和,那些結尾的位置可以用\(f\)算出,所以只需要考慮\(g(g(n))\)怎麼求。
\[g(g(n)) - g(g(n - 1)) = \sum_{i = g(n - 1) + 1}^{g(n)} f(i)\]
\[= \sum_{i = g(n - 1) + 1}^{g(n)} n\]
\[ = n \cdot f(n)\]
所以\(g(g(n)) = \sum_{i = 1}^n i \cdot f(i)\)
然後可以分段計算。

2019.3.8

T1

題意

給定n個數(n爲偶數),從中選取\(\frac{n}{2}\)個數,使得選中數的最大公約數最大。

題解

因爲要選\(\frac{n}{2}\)個數,所以每次隨機一個數,有\(\frac{1}{2}\)的概率選到一個在最優答案集合中的數,那麼我們枚舉這個數的所有約數,對於每個約數check一下就好了。

T2

題意

一個\(n * 6\)的矩陣,在每個格子裏填上\(x \in [1, 6]\),要求任意數與左,右,左上,左下,右上,右下的數字既不能相同,也不能和爲7.
且左上角必須爲1,且按照從上往下逐行再從左往右的順序,第一個2必須要出現在5的前面,第一個3必須要出現在4的前面。
求方案數, 對 1004535809 取模。

題解

,,,並不是很懂
題解說先搞個暴力DP打表,然後用高斯消元or拉格朗日插值找規律。最後發現一個十階遞推式,然後就矩陣快速冪。

T3

題意

提答題。
注意其中的program.exe是可以用來測試的!

題解

接下來說每個點是什麼:

  • 給定一個數,求對某個數取模後的值。
  • 給定一張圖,求圖中橋的個數
  • 求凸包上的點數
  • 求1到n的最短路
  • 最大權匹配
  • 求最長的三位偏序
  • 求最小生成樹的直徑
  • 求單調遞增的子序列數目
  • 求本質不同的子串數目
  • 求生命遊戲若干輪後的生命數目

2019.3.11

T1

題意

一隻螞蟻在樹上走,每個時刻向樹上權值最大的那個點走一步,如果當前節點就是權值最大的節點則不動,權值相同則取標號較大那個點作爲目標點。一開始每個點的權值爲\(s[i]\),每個時刻點\(i\)的權值會增加\(a[i]\).螞蟻一開始在0號節點。
q次詢問,每次求時刻\(i\),螞蟻在哪個節點上。

題解

半平面交……
首先每個點其實就是一條直線,我們的目的是求出每個時刻在最上方的那條線是誰。
所以用半平面交求一下就可以了。
然而考場上想了一個奇怪的分治,,下考後調了幾個小時最後發現權值相同的時候有些情況處理不了??

T2

題意

有7種俄羅斯方塊(有圖,懶得放了,想知道的去玩俄羅斯方塊吧),填滿一個矩形。俄羅斯方塊可以旋轉不能翻轉,矩形中有些位置不能放俄羅斯方塊。
求填滿矩形中所有可以放方塊的位置的方案數。

題解

插頭DP,,,不會

T3

題意

一個人有m種屬性,有n道題,會做一道題意味這個人的屬性全都比這道題的屬性高。
現在已知這個人可以做出前k道,不能做出其餘n - k道。根據給定的題目數據,造出一道新題,這個新題的第i項屬性是給定n道題的第i項屬性的中位數。
你現在不知道人的屬性,需要求有多少種情況,人可以做出那道新題

題解

emmm,,,容斥的題,對每種屬性單獨考慮。
\(f(k)\)表示前\(k\)題會做,後\(n - k\)題隨意的情況。用\(g(k)\)表示前\(k\)題會做,後\(n - k\)題不會做的情況。
然後用廣義容斥推式子,具體內容明天or後天填吧,明天先學學容斥再說。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章