[luogu]10月1日夏令营day1

来源:互联网 发布:怎么学linux 编辑:程序博客网 时间:2024/04/27 18:58

//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 longconst 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;}
原创粉丝点击