1. 程式人生 > >模擬賽簡要題解與心得

模擬賽簡要題解與心得

子集 約數 lib 紀念 機會 分享圖片 printf 關心 stdin

目錄

  • 模擬賽簡要題解與心得
    • T1 方格紙與直線
    • 題解
    • T2 探險
    • 題解
    • 3.蘋果樹
    • 題解
    • 心得

模擬賽簡要題解與心得

最近我的心態炸了,寫此博客紀念調侃一下.

T1 方格紙與直線

【題目描述】
小林有一張 n 行 m 列的方格紙,如下所示。
技術分享圖片
該方格紙黑白相間,且第一行第一列為黑色。頑皮的亮亮在方格紙上畫了一
條連接左上角和右下角的線段。小林看到方格紙後,馬上算出了位於黑色區域的
線段的長度之和占整條線段長度的比值。現在,他想考考你會不會算。
【輸入格式】
一行兩個整數 n 和 m。
【輸出格式】
輸出一個分數,即題目中所求的比值,用兩個由’/’分隔的互質整數表示。
【樣例輸入】
4 6
【樣例輸出】

1/2
【數據規模】
對於 50%的數據,n, m <= 10^6;
對於 100%的數據,1 <= n,m <= 10^9。

題解

找規律的好題,但是還是因為時間問題錯失了滿分的機會.
題目要求的是比值,故當 m 與 n 不互質時,我們可以求出 m 和 n 的最大
公約數 d,並將 m /= d, n /= d,並不影響結果。故我們現在假定 m 和 n 互質。若m 和 n 中有一個為偶數, 那麽根據對稱性, 答案就是 1/2。 如果 m 和 n 均為奇數,
那麽答案就是\((n*m+1) / (2*m*n)\)

T2 探險

【題目描述】
小林和亮亮來到森林中探險, 森林中有一條長度為 S 的小路 (編號從 1 到 S) ,

且在小路上時常會起霧,亮亮也可以用神光讓霧消散。
小林則關心在某一位置的視野。若位置 x 有濃霧,則位置 x 的視野為 0。若
從 x 一直到 S 或從 x 一直到 1 全都沒有濃霧,則視野為INF。其他情況下,位置x的視野為\(max{R - L + 1}\)
要滿足這個區間內沒有濃霧的產生.
具體來說,會有以下事件發生:
1、“1 L R”小路的[L, R]部分產生了濃霧;
2、“2 L R”小路的[L, R]部分濃霧散去了。
3、“3 X” 查詢 X 點的視野。
一開始,小路上沒有任何濃霧。
【輸入格式】
第一行一個整數,為小路的長度 S。
第二行一個整數,為事件數 Q。
接下來 Q 行,每行一個事件,格式如題目描述。
【輸出格式】
對於每一個詢問事件,輸出一個整數或一行字符串“INF”,代表所求視野。
【樣例輸入】
5
5
1 2 4
3 1
3 4
2 3 3
3 3
【樣例輸出】
INF
0
1
【數據規模】
對於 40%的數據,SQ <= 510^7。
對於 100%的數據,2≤S≤100,000,2≤Q≤200,000,1≤L≤R≤S,1≤X≤S。

題解

T1推了很長時間,忘記找規律了,結果T2考場一眼出正解卻不敢寫.寫了分塊.不知道怎麽的就過了.雖然過了,花了我1.5h的時間,so sad.
正解:線段樹,維護區間的和.如果這個點是霧,則這個點的值為1.然後二分最遠到達的l,r即可.
放上分塊的代碼吧:
CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxN = 100000 + 7;
int a[maxN],belong[maxN],R[maxN],L[maxN];
int sum[maxN];
int vis[maxN];
int n;

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

void pushdown(int now) {
    if(vis[now] == 1) {
        for(int i = L[now];i <= R[now];++ i) a[i] = 1;
        sum[now] = R[now] - L[now] + 1;
    }
    if(vis[now] == 2) {
        for(int i = L[now];i <= R[now];++ i) a[i] = 0;
        sum[now] = 0;
    }
    vis[now] = 0;
    return ;
}

inline void change_done(int l,int r) {
    int belong_l = belong[l],belong_r = belong[r];
    if(belong_l == belong_r) {
        if(vis[belong_l]) pushdown(belong_l);
        for(int i = l;i <= r;++ i) a[i] = 1;
        sum[belong_l] = 0;
        for(int i = L[belong_l];i <= R[belong_l];++ i)
            if(a[i]) sum[belong_l] ++;
        return;
    }
    for(int i = belong_l + 1;i < belong_r;++ i) vis[i] = 1,sum[i] = R[i] - L[i] + 1;
    if(vis[belong_l]) pushdown(belong_l);
    if(vis[belong_r]) pushdown(belong_r);
    for(int i = l;i <= R[belong_l];++ i) a[i] = 1;
    for(int i = L[belong_r];i <= r;++ i) a[i] = 1;
    sum[belong_l] = sum[belong_r] = 0;
    for(int i = L[belong_l];i <= R[belong_l];++ i)
        if(a[i]) sum[belong_l] ++;
    for(int i = L[belong_r];i <= R[belong_r];++ i)
        if(a[i]) sum[belong_r] ++;
    return ;
}

inline void change_clear(int l,int r) {
    int belong_l = belong[l],belong_r = belong[r];
    if(belong_l == belong_r) {
        if(vis[belong_l]) pushdown(belong_l);
        for(int i = l;i <= r;++ i) a[i] = 0;
        sum[belong_l] = 0;
        for(int i = L[belong_l];i <= R[belong_l];++ i)
            if(a[i]) sum[belong_l] ++;
        return;
    }
    for(int i = belong_l + 1;i < belong_r;++ i) vis[i] = 2,sum[i] = 0;
    if(vis[belong_l]) pushdown(belong_l);
    if(vis[belong_r]) pushdown(belong_r);
    for(int i = l;i <= R[belong_l];++ i) a[i] = 0;
    for(int i = L[belong_r];i <= r;++ i) a[i] = 0;
    sum[belong_l] = sum[belong_r] = 0;
    for(int i = L[belong_l];i <= R[belong_l];++ i)
        if(a[i]) sum[belong_l] ++;
    for(int i = L[belong_r];i <= R[belong_r];++ i)
        if(a[i]) sum[belong_r] ++;
    return ;
}

inline int query(int pos) {
    int Le = pos,Ri = pos;
    int belong_pos = belong[pos];
    if(vis[belong_pos]) pushdown(belong_pos);
    if(a[pos]) {return 0;}
    if(pos == n || pos == 1) return -1;
    for(int i = pos;i <= R[belong_pos];++ i) {
        if(!a[i]) Ri = i;
        else break;
    }
    for(int i = pos - 1;i >= L[belong_pos];-- i) {
        if(!a[i]) Le = i;
        else break;
    }
    int belong_end = belong[n],belong_begin = belong[1];
    if(vis[belong[Ri + 1]]) pushdown(belong[Ri + 1]);
    if(!a[Ri + 1]) {
        for(int j = belong_pos + 1;j <= belong_end;++ j) {
            if(sum[j] != 0) break;
            Ri = R[j];
        }
        if(vis[belong[Ri + 1]]) pushdown(belong[Ri + 1]);
        for(int j = Ri + 1;j <= n;++ j) {
            if(!a[j]) Ri = j;
            else break;
        }
    }
    if(vis[belong[Le - 1]]) pushdown(belong[Le - 1]);
    if(!a[Le - 1]) {
        for(int j = belong_pos - 1;j >= belong_begin;-- j) {
            if(sum[j] != 0) break;
            Le = L[j];
        }
        if(vis[belong[Le - 1]]) pushdown(belong[Le - 1]);
        for(int j = Le - 1;j >= 1;-- j) {
            if(!a[j]) Le = j;
            else break;
        }
    }
    if(Ri == n || Le == 1) return -1;
    return Ri - Le + 1;
}

int main() {
    freopen("explore.in","r",stdin);
    freopen("explore.out","w",stdout);
    n = read();
    int m = read(),type,l,r,x;
    int q = sqrt(n);
    for(int i = 1;i <= n;++ i) belong[i] = i / q + 1;
    for(int i = 1;i <= n;++ i) R[belong[i]] = i;
    for(int i = n;i >= 1;-- i) L[belong[i]] = i;
    while(m --) {
        type = read();
        if(type == 1) {
            l = read();r = read();
            change_done(l,r);
        }
        if(type == 2) {
            l = read();r = read();
            change_clear(l,r);
        }
        if(type == 3) {
            x = read();
            int tmp = query(x);
            if(tmp == -1) {puts("INF");continue;}
            printf("%d\n", tmp);
        }
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

線段樹代碼(STD):

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#define fortodo(i, f, t) for (i = f; i <= t; i++)
using namespace std;

int lsd[200001], rsd[200001], lsid[200001], rsid[200001], cov[200001], segsize;
bool emp[200001];

int SEG_Build(int L, int R)
{
    int Nid = ++segsize;
    lsd[Nid] = L; rsd[Nid] = R;
    emp[Nid] = true; cov[Nid] = 0;
    if (L == R)
        lsid[Nid] = rsid[Nid] = -1;
    else
        {
            lsid[Nid] = SEG_Build(L, (L + R) / 2);
            rsid[Nid] = SEG_Build((L + R) / 2 + 1, R);
        };
    return Nid;
};

bool SEG_Empty(int Nid)
{
    if (cov[Nid] == 0) return true;
    if (cov[Nid] == 1) return false;
    return emp[Nid];
};

void SEG_Reemp(int Nid)
{
    emp[Nid] = SEG_Empty(lsid[Nid]) && SEG_Empty(rsid[Nid]);
};

void SEG_Inherit(int Nid)
{
    if (cov[Nid] == -1) return;
    if (lsd[Nid] == rsd[Nid]) return;
    cov[lsid[Nid]] = cov[Nid];
    cov[rsid[Nid]] = cov[Nid];
    cov[Nid] = -1;
    SEG_Reemp(Nid);
};

void SEG_Paint(int Nid, int L, int R, int Color)
{
    SEG_Inherit(Nid);
    if ((L == lsd[Nid]) && (R == rsd[Nid]))
        {
            cov[Nid] = Color;
            return;
        };
    int Div = (lsd[Nid] + rsd[Nid]) / 2;
    if (L >  Div) SEG_Paint(rsid[Nid], L, R, Color);
    if (R <= Div) SEG_Paint(lsid[Nid], L, R, Color);
    if ((L <= Div) && (R > Div))
        {
            SEG_Paint(lsid[Nid], L, Div, Color);
            SEG_Paint(rsid[Nid], Div + 1, R, Color);
        };
    SEG_Reemp(Nid);
};

bool SEG_Query(int Nid, int L, int R)
{
    SEG_Inherit(Nid);
    if (SEG_Empty(Nid)) return true;
    if ((L == lsd[Nid]) && (R == rsd[Nid])) return SEG_Empty(Nid);
    int Div = (lsd[Nid] + rsd[Nid]) / 2;
    if (L >  Div) return SEG_Query(rsid[Nid], L, R);
    if (R <= Div) return SEG_Query(lsid[Nid], L, R);
    return SEG_Query(lsid[Nid], L, Div) && SEG_Query(rsid[Nid], Div + 1, R);
};

int S, Q;
int i, j;
int Opt, X, Y;

void Answer(int P)
{
    if (!SEG_Query(1, P, P))
        {
            printf("0\n");
            return;
        };
    if ((SEG_Query(1, 1, P)) || (SEG_Query(1, P, S)))
        {
            printf("INF\n");
            return;
        };
    int L, R, M, Ans[2];
    L = 2; R = P;
    while (L < R)
        {
            M = (L + R) / 2;
            if (SEG_Query(1, M, P))
                R = M;
            else
                L = M + 1;
        };
    Ans[0] = L;
    L = P; R = S - 1;
    while (L < R)
        {
            M = (L + R + 1) / 2;
            if (SEG_Query(1, P, M))
                L = M;
            else
                R = M - 1;
        };
    Ans[1] = L;
    printf("%d\n", Ans[1] - Ans[0] + 1);
};

int main()
{
    freopen("explore.in", "r", stdin);
    freopen("explore.out", "w", stdout);
    scanf("%d%d", &S, &Q);
    segsize = 0;
    SEG_Build(1, S);
    fortodo(i, 1, Q)
        {
            scanf("%d%d", &Opt, &X);
            if (Opt != 3) scanf("%d", &Y);
            if (Opt == 1) SEG_Paint(1, X, Y, 1);
            if (Opt == 2) SEG_Paint(1, X, Y, 0);
            if (Opt == 3) Answer(X);
        };
    return 0;
};

3.蘋果樹

【題目描述】
小林有一棵蘋果樹,樹上一共有 n 個節點,n-1 條邊,每條邊都有長度,且
有些節點上結有蘋果。
亮亮希望砍掉蘋果樹的某些邊,使得沒有任意兩個蘋果在同一聯通塊中,並
且所砍去的邊的長度之和最小。
【輸入格式】
第一行兩個整數 n, k, 分別表示樹的結點數和含有蘋果的結點數。 結點用 0 ~
n-1 標號。
接下來 n-1 行,每行三個數 x, y, z,表示一條從 x 到 y 權值為 z 的邊。
接下來 k 行,每行一個數 x,表示編號為 x 的結點上結有一個蘋果。
【輸出格式】
只有一個整數,表示最小的長度之和。
【樣例輸入】
5 3
2 1 7
1 0 4
2 4 9
1 3 4
0
1
2
【樣例輸出】
11
【數據規模】
對於 40%的數據,n <= 20;
其中 10%的數據和另外 20%的數據,樹的形態為一條鏈;
對於 100%的數據,2 <= n <= 100000,2 <= k <= n, 1 <= 邊權 <= 1000000。

題解

實在是沒有時間寫暴力了,不然40的數據\(2^n\)枚舉子集.再\(n\)判斷聯通塊.
\(60\)的數據直接線段樹即可.
剩下的應該是一個樹形D.P
實在是沒有時間了.
樹形 DP。首先任取一個有蘋果的結點為根,構建整棵樹,接下來自底向上進行動態
規劃。設 f[u]表示以 u 為根的子樹中蘋果互不連通,且子樹中的蘋果不與樹外的蘋果連通的
最小代價。g[u]表示以 u 為根的子樹中蘋果互不連通所需的最小代價。
那麽當 u 上有蘋果時,
g[u] = sum (f[v]) (v 為 u 的所有子節點) ;
f[u] = g[u] + pre[u] (pre[u]為 u 與其父節點連邊的權值) 。
當 u 上無蘋果時,
設 s[u] = sum (f[v]) (v 為 u 的所有子節點)
則 g[u] = min (s[u] - f[v] + g[v])
f[u] = min(s[u], g[u] + pre[u)]

心得

考場策略出現問題,T1沒有找規律,T2一眼題沒有寫正解,T3沒有時間看.
開場肝T1.
直接sb了.
以後模擬賽時應該通讀一遍題目.
希望以後不犯類似的錯誤.

模擬賽簡要題解與心得