1. 程式人生 > >[luogu]10月1日夏令營day1

[luogu]10月1日夏令營day1

//10月1去了清北,又沒上成洛谷
//作為洛谷的忠實粉真不應該
//今天做了做

T1

(想了解這個直接搜題目名即可)

題目描述
我妻蛤乃給你出了一道送命題:
黃梅時節家家雨,青草池塘處處蛙~
有n只青蛙,第i只青蛙會每過xi秒會連續叫yi秒。然而由於青蛙的壽命在增加,所以從第二次開始每次休息結束
後這隻青蛙連續叫的時間會增加zi秒。
給定n只青蛙,每一隻的xi,yi,zi,以及時間t,求在前t秒中,所有青蛙共叫了多少秒。

輸入輸出格式
輸入格式:
第一行兩個數n和t
之後n行,第i+1行每行三個非負整數xi,yi,zi
輸出格式:
一行一個數表示答案

輸入輸出樣例
輸入樣例#1

【子任務】
子任務會給出部分測試資料的特點。 如果你在解決題目中遇到了困難, 可以嘗試只解決一部分測試資料。

8 10
9 1 1
1 9 9
4 1 0
2 3 3
1 0 0
1 4 0
9 2 5
1 2 1
輸出樣例#1:

34
輸入樣例#2:

1 233333
233 233 233
輸出樣例#2:

223081
輸入樣例#3:

10 100000000
1 0 0
1 0 5
1 2 2
1 2 8
1 3 0
1 5 0
1 5 2
1 5 5
1 7 0
1 8 3
輸出樣例#3:

845787522

每個測試點的資料規模及特點如下表:
測試點編號  n的範圍  t的範圍  特殊性質
測試點1 n = 1
測試點2 n = 100 t <= 100 x = 0
測試點3 n = 100 y = 0
測試點4 n = 100 z = 0
測試點5 n = 100
測試點6 n = 100000 t <= 100 x = y = z
測試點7 n = 100000 t <= 100 z = 0
測試點8 n = 100000 y = 0
測試點9 n = 100000 t <= 100000
測試點10 n = 100000
對於100%的資料,n <= 100000 , t <= 2000000000,x + y + z > 0
0 <= x , y , z <= 2000000000
【說明】
【樣例1說明】
每隻青蛙分別叫了1,9,2,6,0,8,1,7秒
【樣例2說明】
那隻青蛙叫了223081秒
【樣例3說明】
每隻青蛙分別叫了
0,99993675,99990000,99994999,75000000,83333333,99990002,99993676,87500000,99991837秒

其實看到這道題就能想到用前n項和求
後來想了想爆搜會超時,可以用二分來優化下
注意二分時求前n項和時的邊界,可以用 t/(X+Y+Z)來當,要是太大可能會爆long long(我第一次70分就是爆了long long)

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

long long n,t;

long long read() {
    long long x = 0, f = 1;
    char
ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + ch - '0'; ch = getchar(); } return x * f; } long long x,y,z,sum = 0; bool pd(long long mid) { return (x + y) * mid + ( mid * (mid - 1) ) / 2 * z <= t; } void work() { x = read(), y = read(), z = read(); long long l = 0, r = t / (x + y + z),ans = 0; while(l <= r) { long long mid = (l + r) >> 1; if(pd(mid)) { ans = mid; l = mid + 1; } else r = mid - 1; } long long p = y * ans + ( ans * (ans - 1) ) / 2 * z; sum += p; p = t - p - x * ans - x; if(p > 0) sum += p; } int main() { n = read(), t = read(); for(int i = 1; i <= n; i++) { work(); } cout<<sum<<endl; return 0; }

T2

題目描述
資料結構大師ddd給你出了一道題:
給你一棵樹,最開始點權為0,每次將與一個點x樹上距離<=1的所有點點權+1,之後詢問這些點修改後的點權

輸入輸出格式

輸入輸出格式
輸入格式:
第一行兩個數n和m

之後一行n-1個數,第i個數fa[i + 1]表示i + 1點的父親編號,保證fa[i + 1] < i + 1
之後一行m個數,每個數x依次表示這次操作的點是x
輸出格式:
輸出一個數,即這m次詢問的答案的和
保證答案在有符號64位整數範圍內

輸入輸出樣例

說明
樣例#3,#4,#5,#6見下發的檔案
【子任務】
子任務會給出部分測試資料的特點。
如果你在解決題目中遇到了困難, 可以嘗試只解決一部分測試資料。
每個測試點的資料規模及特點如下表:
測試點編號 n的範圍 m的範圍 特殊性質 
測試點1 n = 1000 m = 1000 資料隨機
測試點2 n = 1000 m = 1000 資料隨機
測試點3 n = 100000 m = 100000

輸入樣例#1:

6 3
1 1 2 3 3
1 2 3
輸出樣例#1:

15
輸入樣例#2:

6 10
1 1 2 3 3
1 4 6 5 2 3 3 3 3 3
輸出樣例#2:

115

第4頁 共7頁

測試點編號 n的範圍 m的範圍 特殊性質 
測試點4 n = 100000 m = 100000
測試點5 n = 100000 m = 1000000 樹是一條鏈
測試點6 n = 100000 m = 1000000
測試點7 n = 100000 m = 1000000
測試點8 n = 100000 m = 3000000
測試點9 n = 100000 m = 3000000
測試點10 n = 100000 m = 10000000

//表示賽後看std的方法沒看懂
//但我的方法也A了

記錄每個點在最後處理完的狀態
可以將那個點改變過記錄下來,然後dfs一遍
注意題目 保證fa[i + 1] < i + 1 所以只用建單向邊就好
然後對於每個點對答案的貢獻就是f[i] * (f[i] + 1) / 2

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

const int maxn = 100000 + 100;
int n,m;
struct edge {
    int u,v;
    int next;
}e[maxn << 1];
int head[maxn], tot = 0;

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

void add(int u, int v) {
    e[++tot] = (edge){u,v,head[u]};
    head[u] = tot;
}

int val[maxn],f[maxn];

void dfs(int x, int fa) {
    f[x] += val[x] + val[fa];
    for(int i = head[x]; i; i = e[i].next) {
        int v = e[i].v;
        f[x] += val[v];
        dfs(v,x);
    }
}

int main() {
    n = read(), m = read();
    for(int i = 2; i <= n; i++) {
        int v = read();
        add(v,i);
    }
    for(int i = 1; i <= m; i++) {
        int a = read();
        val[a]++;
    }
    dfs(1,0);
    long long sum = 0;
    for(int i = 1; i <= n; i++) {
        sum += (long long)f[i] * (f[i] + 1) / 2;
    }
    cout<<sum<<endl;
    return 0;
}

T3

題目描述
江爺爺給你出了一道題:
給你一個圖,保證每個點最多屬於一個簡單環,每個點度數最多為3,求這個圖有多少“眼鏡圖形個數”
保證圖聯通哦~
其中“眼鏡圖形個數”,定義為三元組(x,y,S),其中x和y表示圖上的兩個點,S表示一條x到y的簡單路徑,而且必
須滿足:
1.x和y分別在兩個不同的簡單環上
2.x所在的簡單環與路徑S的所有交點僅有x,y所在的簡單環與路徑S的所有交點僅有y。
(x,y,S)與(y,x,S)算同一個眼鏡
如果你無法理解,可以參考樣例。
保證圖是聯通的

輸入輸出格式
輸入格式:
第一行兩個數n和m
之後m行,每行兩個數x,y表示x和y之間有一條邊。
輸出格式:
輸出一個數,表示眼鏡的個數對19260817取膜的結果

輸入輸出樣例
輸入樣例#1:

說明
樣例#3,#4,#5,#6見下發的檔案
非常抱歉,出了點小鍋,sample5.out好像是空檔案,應該是6734568
【子任務】

11 12
1 2
2 3
3 4
4 5
5 1
4 6
6 7
7 8
8 9
9 10
10 11
11 7
輸出樣例#1:

1
輸入樣例#2:

14 16
1 2
2 3
3 4
4 1
3 5
5 6
6 7
7 8
8 9
9 6
9 13
13 14
13 10
10 11
11 12
12 10
輸出樣例#2:

4

子任務會給出部分測試資料的特點。
如果你在解決題目中遇到了困難, 可以嘗試只解決一部分測試資料。
測試點編號  n的範圍    m的範圍     特殊性質
測試點1 n <= 10 m <= 20
測試點2 n <= 20 m <= 40
測試點3 n <= 20 m <= 40
測試點4 n <= 2000 m <= 4000
測試點5 n <= 2000 m <= 4000
測試點6 n <= 1000000 m <= 2000000 簡單環個數 <= 2000
測試點7 n <= 1000000 m <= 2000000 簡單環個數 <= 2000
測試點8 n <= 1000000 m <= 2000000
測試點9 n <= 1000000 m <= 2000000
測試點10 n <= 1000000 m <= 2000000
//十月1回來後某dalao就讓我看這個題,說是樹形DP讓我推推

首先縮點,注意是無向圖不能直接套tarjan
然後就是噁心的樹形DP
用f[x]表示以x為根的子樹,到x構成的“一半的眼鏡”的數量
注意根節點是不是環,如果是環要特殊處理

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define ll long long

const int maxn = 1000000 + 100;
const int mod = 19260817;
int n,m;
int x[maxn << 1],y[maxn << 1];
vector<int>q[maxn];

int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * f;
}
//縮點
int vis[maxn],f[maxn],belong[maxn],col[maxn],cnt = 0;
void dfs(int x) {
    vis[x] = 1;
    for(int i = 0; i < q[x].size(); i++) {
        int v = q[x][i];
        if(v == f[x]) continue;
        if(!vis[v]) f[v] = x, dfs(v);
        else if(!belong[v]) {
            int t = x;
            col[++cnt] = 1;
            while(1) {
                belong[t] = cnt;
                if(t == v) break;
                t = f[t];
            }
        }
    }
}

struct edge {
    int u,v,next;
}e[maxn << 1];
int head[maxn],tot = 0;

void add(int u, int v) {
    e[++tot] = (edge){u,v,head[u]};
    head[u] = tot;
}

int fa[maxn],dp[maxn];
ll ans = 0;

void DP(int x) {
    for(int i = head[x]; i ; i = e[i].next) {
        int v = e[i].v;
        if(v != fa[x]) {
            fa[v] = x;
            DP(v);
            ans = (ans + (ll)dp[v] * dp[x] * (col[x] ? 2 : 1) % mod) % mod;
            dp[x] = (dp[x] + dp[v]) % mod; 
        }
    }
    if(col[x]) {
        ans = (ans + dp[x]) % mod;
        dp[x] = (dp[x] * 2 + 1) % mod;
    }
}

int main() {
    n = read(), m = read();
    for(int i = 1; i <= m; i++) {
        x[i] = read(), y[i] = read();
        q[x[i]].push_back(y[i]);
        q[y[i]].push_back(x[i]);
    }
    dfs(1);

    for(int i = 1; i <= n; i++) if(!belong[i]) belong[i] = ++cnt;
    for(int i = 1; i <= m; i++) {
        if(belong[x[i]] != belong[y[i]]) {
            add(belong[x[i]],belong[y[i]]),add(belong[y[i]],belong[x[i]]);
        }
    }
    DP(1);
    cout<<ans;
    return 0;
}