[luogu]10月1日夏令营day2

//难度上还行,T3有点坑

#T1#
题目描述
59式给你出了一道**题:
有n个防御人(守方)还有n个攻击坦克(攻方)
每个防御人有护甲a[i],而攻击方每个坦克有火力b[i]
如果一个防守方的护甲不小于攻击方的攻击力,就可以防的住
然而我们保持了最大限度的克制,所以每个攻击方的攻击力都减去了t
而且防守方有zcy所以你有x次机会加强一个人的护甲,把它的护甲变成y
求安排一个防御的方案,一个人正好对一个坦克,使得t尽可能的小,并且满足没有人被坦克打败。
然而由于zcy吊打集训队,所以不要求你输出方案,只要求输出最小的可能的t
这个t是根据你的人和坦克的配对方案而变化的,不是人为给出的
可以认为你给出了一个方案后,某某某才确定出了这个t值,而你要让某某某确定出的这个t值最小
也就是说这个题求的就是那个最小的t
输入输出格式
输入格式:
第一行三个数n,x,y
之后一行n个数表示每个守方的护甲
之后一行n个数表示每个攻击坦克的火力
输出格式:
一行一个数表示答案

输入输出样例
说明
样例#3,#4,#5,#6见下发的文件
【子任务】
子任务会给出部分测试数据的特点。
如果你在解决题目中遇到了困难, 可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
测试点编号 n的范围   x的范围   y的范围
输入样例#1:
2 0 0
6 4
8 9
输出样例#1:
4
输入样例#2:
8 6 4
1 9 2 6 0 8 1 7
6 4 6 4 8 9 8 9
输出样例#2:
2
测试点编号 n的范围   x的范围   y的范围
测试点1 n = 10 x = 0 无限制
测试点2 n = 10 x = 0 无限制
测试点3 n = 10 无限制 y = 0
测试点4 n = 10 无限制 y = 0
测试点5 n = 1000 x = 0 无限制
测试点6 n = 1000 x = 0 无限制
测试点7 n = 1000 无限制 y = 2000000000
测试点8 n = 1000 x = n 无限制
测试点9 n = 100000 无限制 无限制
测试点10 n = 200000 无限制 无限制
对于100%的数据,n <= 200000 , 0<=ai,bi,x,y<=2000000000
【说明】
【样例1说明】
当克制值t为4的时候
用守卫4挡坦克8,守卫6挡坦克9
可以挡住
【样例2说明】
当克制值t为2的时候
把守卫0,1,1,2都变成4
之后按如下顺序排列守方和攻击方
9 4 6 4 4 8 7 4
9 6 8 4 6 9 8 4

就是贪心
sort一下
把a中小y的前x个换成y
找一下b[i] - a[i]差的最小值

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

const int maxn = 200000 + 100;
int n,x,y;
int a[maxn],b[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 main() {
	n = read(), x = read(), y = read();
	for(int i = 1; i <= n; i++) a[i] = read();
	for(int i = 1; i <= n; i++) b[i] = read();
	sort(a + 1, a + 1 + n);
	sort(b + 1, b + 1 + n);
	for(int i = 1 ; i <= x; i++) {
		if(a[i] >= y) break;
		a[i] = y;
	}
	sort(a + 1, a + 1 + n);
	int ans = 0;
	for(int i = 1; i <= n; i++) {
		ans = max(b[i] - a[i],ans);
	}
	cout<<ans;
	return 0;
}

#T2#
给你出了一道题:
给你一个长为n的序列v,你可以从里面选出最多m个数
有一个数a,以及b个规则,每个规则即为:
对于这个序列的所有长为a的连续子区间,
如果这个子区间中对应的给出的x个位置都被选中了,
则这次选择的分数加上y(y可能为负数,这种情况下分数仍然要加上y)

输入输出格式
输入格式:
第一行四个数n,m,a,b
之后一行n个数表示序列v
之后输入b个规则
每个规则先输入两个数x和y,x != 0
之后一行x个数,分别表示这给定的x个位置
输出格式:
一行一个数表示最大可能得到的分数

输入输出样例
输入样例#1:

5 3 3 1
2 3 3 3 3
2 233
1 3
输出样例#1:

474
输入样例#2:

5 3 3 1
111 222 333 444 555
2 52
1 3

输出样例#2:

1384
输入样例#3:

6 3 3 2
1 2 3 3 4 1
2 1
1 2
2 2
2 3
输出样例#3:

16

说明
样例#4,#5,#6见下发的文件
【子任务】
子任务会给出部分测试数据的特点。
如果你在解决题目中遇到了困难, 可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
测试点编号n的范围  m的范围  a的范围  b的范围
测试点1 n <= 20 m <= 20 a <= 10 b <= 5
测试点2 n <= 20 m <= 20 a <= 10 b <= 5
测试点3 n <= 40 m <= 40 a <= 10 b <= 100000
测试点4 n <= 40 m <= 40 a <= 10 b <= 100000
测试点5 n <= 100 m <= 50 a <= 10 b = 0
测试点6 n <= 100 m <= 50 a <= 10 b = 0
测试点7 n <= 100 m <= 50 a <= 10 b <= 100000
测试点8 n <= 100 m <= 50 a <= 10 b <= 100000
测试点9 n <= 100 m <= 50 a <= 16 b <= 100000
测试点10 n <= 100 m <= 50 a <= 16 b <= 100000
对于100%的数据,n <= 100 , m <= 50 ,a <= 16 , b <= 100000 , 序列中每个值以及每条规则带来的加分的
绝对值 <= 100

//表示题意不好理解
//理解后推了推DP,考虑过三维但状压太弱了不会写

f[i][j][k] 表示前i个位置选了j个位置,k为状压的序
f[i+1][j][t] , f[i][j][s] + ( i + 1 >= a ? value[t] : 0 )
f[i+1][j + 1][t | 1] , f[i][j][s] + A[i + 1] + ( i + 1 >= a ? value[t | 1] : 0 )

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define deg(i) cout<<'#'<<i<<endl;

int n,m,a,b;
int col[233],val[1<<16],vall[1<<16];
int f[55][1<<16],g[55][1<<16];

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 main() {
    n = read(),m =read(),a = read(),b = read();
    for(int i = 1; i <= n; i++) col[i] = read();
    for(int i = 1; i <= b; i++) {
        int x = read(), y = read(),p = 0;
        while(x--)	p |= 1 << (a - read());
        vall[p] += y;
    }
    for(int i = 0; i < (1 << a); i++) {
        for(int j = i;  j; j = (j-1) & i) {
            val[i] += vall[j];
        }
    }
    memset(f,-0x3f,sizeof(f));
    memset(g,-0x3f,sizeof(g));
    f[0][0] = 0;
    int v = (1<<a) - 1;
    for(int i = 0; i < n; i++,swap(f,g),memset(g,-0x3f,sizeof(g))) {
        for(int j = 0; j <= min(m,i + 1); j++) {
            for(int k = 0; k <= v; k++) {
                if(f[j][k] != -0x3f) {
                    int t = (k << 1) & v;
                    g[j][t] = max(g[j][t], f[j][k] + (i + 1 >= a ? val[t]: 0));
                    g[j+1][t | 1] = max(g[j + 1][t | 1], f[j][k] + col[i + 1] + (i + 1 >= a ? val[t | 1] : 0));
                }
            }
        }
    }
    
    int ans = 0;
    for(int i = 0; i <= m; i++) {
        for(int j = 0; j <= v; j++) {
            ans = max(ans,f[i][j]);
        }
    }
    cout<<ans;
    return 0;
}

#T3#
Lass给你出了一道倒闭题:
给你一个n个点的树还有一只青蛙。
由于这棵树被夺去了生命,所以你需要通过青蛙来复活上面的节点,有m次操作。
每次操作有三个参数a,b,t
然后会给你a条链,青蛙都会把每条链上面的所有点增加一秒
然后会给你b个点,青蛙都会把每个点子树里面的所有点增加一秒
注意青蛙是大公无私的,所以每次牠不会管这个节点之前有没有增加过,都会增加一秒
但是因为树失去了生命太多了,所以只有被增加了>=t秒的节点会重获新生(t可以为0)
而且因为你的黑框眼镜是假的,所以每次的操作是独立的
也就是说,每次操作增加的秒数,在这次操作结束的瞬间就会被青蛙回收,这棵树的所有节点又会失去生命
多么残酷的一道题啊

输入输出格式
输入格式:
第一行二个数n,m
之后n-1行每行两个数x,y表示x和y直接连有边
之后m次操作
每次操作先是一行三个数a,b,t,意义已上述
之后a行每行两个数x,y表示青蛙从x跳到y,每个节点增加了1s
之后b行每行一个数x表示青蛙把x的子树里面每个节点增加了1s

第4页 共9页

输出格式:
m行,第i行一个数表示第i次操作有多少个节点获得了新生

输入输出样例
输入样例#1:

5 2
1 2
2 3
3 4
4 5
1 1 2
2 3
3
1 2 2
1 3
2
5
输出样例#1:

1
3
输入样例#2:

5 2
1 2
2 3
2 4
3 5
2 3 3
2 3
3 3
3
3
3
4 2 3
1 4
2 3
4 5
1 2
4
3
输出样例#2:

第5页 共9页

说明
样例#3,#4,#5,#6,#7见下发的文件
【由乃暖心的小提示】
这个题最大读入量有约24MB
经实测
由于luogu的评测姬非常快,所以这个题里面
用getchar的读入优化和fread的差距不到200ms
所以我这次不提供fread模版了
如果有兴趣使用可以粘上场比赛发的那个
然后std用时800ms,大家可以根据这个估算时间
注意到了真正的NOIP出题人肯定不会这么良心的
所以不可能告诉你读入量多少,std用时多少这种事情,需要自己去判断的
【子任务】
子任务会给出部分测试数据的特点。
如果你在解决题目中遇到了困难, 可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
测试点   n     m     a的和     b的和    t     特殊性质
1 100 10 50 50 <=1 数据随机
2 100 10 50 50 <=1 数据随机
3 100 10 50 50 无限制 数据随机
4 100 10 50 50 无限制 无
5 1000 200 600 400 <=1 链
6 2000 5000 500000 500000 无限制 链
7 5000 5000 500000 500000 无限制 无
8 5000 5000 500000 500000 无限制 无
9 5000 5000 500000 500000 无限制 无
10 100000 20000 49784 50216 无限制 链
11 100000 20000 50306 49694 无限制 无
12 100000 25000 200000 0 无限制 无
13 100000 60000 300000 300000 无限制 无
14 100000 80000 0 400000 无限制 链
15 100000 80000 0 400000 <=1 数据随机
16 100000 80000 400000 400000 无限制 数据随机
17 100000 8000 400000 0 无限制 无
18 100000 10000 0 1000000 无限制 无
19 100000 100000 500000 500000 无限制 无
20 100000 100000 1000000 1000000 无限制 链
21 100000 100000 1000000 1000000 无限制 链
22 100000 100000 1000000 1000000 无限制 链
23 100000 100000 1500000 1000000 无限制 无
24 100000 200000 1500000 400000 无限制 无
25 100000 400000 1500000 1000000 无限制 无
对于100%的数据,n <= 100000 , m <= 400000 , a的和 <= 1500000 , b的和 <= 1000000

//蒟蒻就是蒟蒻,自己自信的算了算复杂度以为能A
//结果才有36分
//果然是T3,正解是什么鬼????

36分
暴力找LCA
对于每组数据进行树上差分

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

const int maxn = 200000 + 100;
int n,m;
struct edge {
	int u,v;
	int next;
}e[maxn];
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 f[maxn][30],dep[maxn],vis[maxn],dis[maxn];

void dfs(int x) {
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].v;
		if(!vis[v]) {
			f[v][0] = x;
			dep[v] = dep[x] + 1;
			vis[v] = 1;
			dfs(v);
		}
	}
}

void init() {
	dfs(1);
	for(int j = 1; j <= 20; j++) {
		for(int i = 1; i <= n; i++) {
			f[i][j] = f[ f[i][j-1] ][j-1];
		}
	}
}

int Lca(int x, int y) {
	if(dep[x] < dep[y]) swap(x,y);
	for(int i = 20; i >= 0; i--) {
		if(dep[f[x][i]] >= dep[y]) {
			x = f[x][i];
		}
	}
	if(x == y) return x;
	for(int i = 20; i >= 0; i--) {
		if(f[x][i] != f[y][i]) {
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}

void dfs_(int x) {
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].v;
		if(v != f[x][0]) {
			dis[v] += dis[x];
			dfs_(v);
			vis[x] += vis[v];
		}
	}
}

void work() {
	memset(vis,0,sizeof(vis));
	memset(dis,0,sizeof(dis));
	int la = read(), lb = read(), tim = read();
	for(int i = 1; i <= la; i++) {
		int u = read(), v =read();
		int p = Lca(u,v);
		vis[u]++,vis[v]++,vis[p]--,vis[f[p][0]]--;
	}
	for(int i = 1; i <= lb; i++) {
		int u = read();
		dis[u]++;
	}
	dfs_(1);
	int ans = 0;
	for(int i = 1; i <= n; i++) {
		if(vis[i] + dis[i] >= tim) ans++;
	}
	printf("%d\n",ans);
}

int main() {
	n = read(), m = read();
	for(int i = 1; i < n; i++) {
		int u = read(), v = read();
		add(u,v), add(v,u);
	}
	vis[1] = 1, dep[1] = 1;
	init();
	for(int i = 1; i <= m; i++) {
		work();
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值