[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
#include
#include
#include
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<

#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
#include
#include
#include
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<

#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
#include
#include
#include
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];
vectorq[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<

你可能感兴趣的:(#,Noip)