//难度上还行,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;
}