NOIP模拟 17.8.18
NOIP模拟17.8.18
A.小菜一碟的背包
【题目描述】
Blice和阿强巴是好朋友
但萌萌哒Blice不擅长数学,所以阿强巴给了她一些奶牛做练习
阿强巴有 n头奶牛,每头奶牛每天可以产一定量的奶,同时也需要一定量的草作为饲料
对于第 i头奶牛来说,它每天可以产 vi升的奶,同时需要 wi千克的草作为饲料
现在来自蚯蚓国的九条可怜想借一些奶牛,使借走的这些奶牛每天的总产奶量最大,但九条可怜很穷,每天最多只能提供W
千克的草作为饲料,而且她还需要对付跳蚤国的神刀手,所以她把这个问题交给了阿强巴,不不不……阿强巴觉得这个
问题太简单了,所以把这个问题交给了你
【输入格式】
第一行两个整数 n,W,表示奶牛的数量和每天最多能提供的草
接下来 n行,每行两个整数,第 行表示第vi 头奶牛的产奶量 和食量wi
【输出格式】
仅一行,输出一个整数,表示每天最大的总产奶量
【输入样例】
8 40
10 9
12 11
11 12
10 10
8 11
7 9
8 10
9 10
【输出样例】
41
【限制与约定】
对于 20 %的数据,n<=10
另有 30%的数据,W<=10000
另有 10%的数据,wi = w1
对于 100%的数据,1<=n<=100,1<=wi,W<=1e9,1<=vi<=1e7
对于所有数据,均满足特殊限制:w1<=wi<=w1+3
【题解】
此题有一个十分强大的限制:对于所有数据,w1<=wi<=w1+3
这也就意味着,物体体积只有0,1,2,3三种。
但是,如何让他们满足限制W?我们知道,如果只选一种物品,那么体积限制是W-w1;选两种物品,体积限制是W-w1*2.。。。
所以,加一维,表示已经选的物品个数即可
f[i][j][k]表示DP到第i个奶牛时,选了j个奶牛,这j个奶牛(减去base之后的)w_i的和为j
转移即可
- #include <iostream>
- #include <cstdio>
- #include <cstdlib>
- #include <cstring>
- #define min(a, b) ((a) < (b) ? (a) : (b))
- #define max(a, b) ((a) > (b) ? (a) : (b))
- const int MAXN = + ;
- const int INF = 0x3f3f3f3f;
- inline void read(long long &x)
- {
- x = ;char ch = getchar(), c = ch;
- while(ch < '' || ch > '')c = ch, ch = getchar();
- while(ch <= '' && ch >= '')x = x * + ch - '', ch = getchar();
- if(c == '-')x = -x;
- }
- long long n,W,v[MAXN],w[MAXN],dp[][MAXN][MAXN * ],mi,ans,sum;
- int main()
- {
- read(n), read(W);
- mi = INF;
- for(register int i = ;i <= n;++ i) read(v[i]), read(w[i]), mi = min(mi, w[i]);
- for(register int i = ;i <= n;++ i) w[i] -= mi, sum += w[i];
- int p = ;
- for(register long long i = ;i <= n;++ i, p ^= )
- for(register long long j = ;j <= i;++ j)
- for(register long long k = ;k <= sum;++ k)
- {
- dp[p][j][k] = dp[p ^ ][j][k];
- if(j*mi + k <= W && k >= w[i])
- {
- dp[p][j][k] = max(dp[p][j][k], dp[p ^ ][j - ][k - w[i]] + v[i]);
- ans = max(ans, dp[p][j][k]);
- }
- }
- printf("%d", ans);
- return ;
- }
T1
B.整齐划一的子树
【题目描述】
蛐蛐国王讨厌冗长的题面,所以他要把题面写的简单一点。
给定一棵 n个节点的树,树上的每个节点 i有一个权值 ai,每次操作中你可以选择一个包含1号节点的连通子树,将这个连
通子树上所有节点的权值加上(或减去)一个相同的非负整数,要求将所有节点变为0,且最小化所有加上(或减去)的
非负整数之和
【输入描述】
第一行一个整数 n,表示这棵树的节点个数。
第二行 n个整数,第 i个整数表示节点i 上的权值 ai。
接下来共n-1 行,每行两个整数ui,vi ,表示第 条边上连接ui,vi 这两个顶点。
【输出格式】
仅一行一个整数,表示最小答案。
【样例输入】
3
2 ‐2 2
1 2
1 3
【样例输出】
6
【样例解释】
第一次选择节点 构成的联通子树,权值都加上2;
第二次选择节点 构成的联通子树,权值都减去2;
第三次选择节点 ,权值减去2。
【限制与约定】
对于 30% 的数据,n ≤ 1000
对于另外15% 的数据,ui = i, vi = i + 1(1 ≤ i ≤ n − 1)
对于另外15% 的数据,ui = 1, vi = i(2 ≤ i ≤ n)
对于 的数据100%, n ≤ 300000 , |ai| ≤ 1e9,ui, vi ≤ n ,保证给定数据是一棵树
提示
设原树点集为 V,边集为 W,联通子树定义为选择子集 S ⊆ V,使得对于任意一对点 u,
存在边u, v ∈ S(u ≠ v) e(u, v) ∈ E
通俗来说,就是在原树中选择一些点,使得这些点所发出的边仍能将它们连通起来。
由于输入文件可能较大,所以请使用scanf或其他更快速的方法进行读入操作
【题解】
这是一道很裸的树形DP题,虽然我们做出来。
递归的想,要先把叶子节点变为0,再把他们的父亲变0,再...
dp1[i]表示把i和他的子树变为0的最小累加值
dp1[i]表示把i和他的子树变为0的最小累减值
转移:dp1[i] = max(dp1[i], dp1[son]) dp2[i] = max(dp2[i], dp2[son])
为什么取max?我们可以把操作想成每次只能+1或-1。需要做的次数决定于最大的那个,
最小的那个可以少做几次
这样就得到节点i的累加累减值,操作后i节点权值变为:t = value[i] - dp2[i] + dp1[i]
若t > 0,则还需减少t,dp2[i] += t
若t < 0,则还需增加t,dp1[i] -= t,注意t是负数
若t = 0,则不需修改
- #include <iostream>
- #include <cstdio>
- #include <cstdlib>
- #include <cstring>
- #define max(a, b) ((a) > (b) ? (a) : (b))
- const long long MAXN = + ;
- inline void read(long long &x)
- {
- x = ;char ch = getchar(), c = ch;
- while(ch < '' || ch > '')c = ch, ch = getchar();
- while(ch <= '' && ch >= '')x = x * + ch - '', ch = getchar();
- if(c == '-')x = -x;
- }
- struct Edge
- {
- long long u,v,next;
- Edge(long long _u, long long _v, long long _next){u = _u;v = _v;next = _next;}
- Edge(){}
- }edge[MAXN << ];
- long long head[MAXN], cnt;
- void insert(long long a, long long b)
- {
- edge[++cnt] = Edge(a, b, head[a]);
- head[a] = cnt;
- }
- long long value[MAXN],dp1[MAXN],dp2[MAXN],n,b[MAXN];
- void dfs(long long u)
- {
- b[u] = ;
- for(register long long pos = head[u];pos;pos = edge[pos].next)
- {
- long long v = edge[pos].v;
- if(b[v])continue;
- dfs(v);
- dp1[u] = max(dp1[u], dp1[v]);
- dp2[u] = max(dp2[u], dp2[v]);
- }
- long long tmp = value[u] + dp1[u] - dp2[u];
- if(tmp > )dp2[u] += tmp;
- else dp1[u] -= tmp;
- return;
- }
- int main()
- {
- read(n);
- register long long tmp1, tmp2;
- for(register long long i = ;i <= n;++ i) read(value[i]);
- for(register long long i = ;i < n;++ i) read(tmp1), read(tmp2), insert(tmp1, tmp2), insert(tmp2, tmp1);
- dfs();
- printf("%lld", dp1[] + dp2[]);
- return ;
- }
T2
简明易懂的序列
【题目描述】
为了体现出题人的友善以及照应题目名称,我们将题意浓缩为一句话
求给定长度为n 的数字序列a 中最长严格下降子序列的长度以及符合该长度且本质不同的严格下降子序列个数
【输入描述】
第一行一个整数 n,表示序列的长度
第二行共 n个整数,第i 个数表示ai
【输出描述】
仅一行,共两个整数,分别表示最长严格下降子序列的长度和符合条件的严格下降子序列个数,两数间用一个空格隔开
由于符合该长度的严格下降子序列个数可能很多,你只需要输出它对1000000007 取模后的结果
【输入样例1】
4
2 1 100 99 100 99
【输出样例1】
2 2
【输出样例2】
10
5 8 7 11 2 4 9 1 3 7
【输出样例2】
4 3
【样例解释】
样例1中最长严格下降子序列长度为 2,符合条件的严格下降子序列为{2,1} 和{100,99}
样例2中最长严格下降子序列长度为 4,符合条件的严格下降子序列为{8,7,2,1} 和 {8,7,4,3} 和{8,7,4,1}
【限制与约定】
对于 20%的数据, n ≤ 10
对于 40%的数据, n ≤ 4000
另有20% 的数据,保证只有一个严格下降子序列符合条件
对于100% 的数据, 1 ≤ n,ai ≤ 300000
【提示】
子序列指在原序列 a中选出一些元素,按原位置关系不变而形成的新的序列b
当一个长度为 n的序列 a符合a1 > a2 > a3 >...> an ,我们称序列 a是严格下降的
【题解】
第一问nlogn做法不在多言。用f[i]表示长度为i的最后一个元素的最大值
第二问,在f[i]的每一个i上维护一个出现过的值得队列,可知这个队列是单调非减的。
g[i]表示以队列中q[i]这个元素结尾的最长严格下降子序列个数
假如f[j]=k-1,f[i]=k,j<i,但a[j]<=a[i],显然j不能作为i的转移点,那么对于比i大的那
些i’(f[i’]=k),j更不可能是i’的转移点了,因为a[i’]>=a[i]
q[i]存储f[]=i的点
我们在转移f[i]时,去寻找q[f[i]-1]的队首,如果队首head的a[head]<=a[i],那就弹出队首,循环该过程直到队为空或a[head]>a[i]
转移完成后把i压入q[f[i]]队尾即可
再开个数组记录每个队列中g[i]的和即可
到此问题并没有完成,因为没有考虑a[i]重复的情况,即方案数计算可能重复
当j<k,a[j]=a[k],f[j]=f[k]时,我们肯定要从k转移,因为k在j后面,所以g[k]>=g[j]
所以我们要保证每个队列中的点a[i]单调递增,而不是单调不降
把i压入相应队列时,还要把那些和a[i]相等的队尾元素弹出
开一个双端队列即可,可以用STL的deque也可以手写
——张浩南
我这里为了方便处理,数据是倒着读的,这样就改成了求“严格单调递增”了。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cstdlib>
- #include <algorithm>
- #define max(a, b) ((a) > (b) ? (a) : (b))
- inline void read(long long &x)
- {
- x = ;char ch = getchar(),c = ch;
- while(ch < '' || ch > '')c = ch, ch = getchar();
- while(ch <= '' && ch >= '')x = x * + ch - '', ch = getchar();
- if(c == '-')x = -x;
- }
- const long long MAXN = + ;
- const long long MOD = ;
- long long data[MAXN],cnt,pre[MAXN],next[MAXN];
- struct Queue
- {
- long long size,head,tail;
- void push_back(long long x)
- {
- data[++cnt] = x;
- if(!size)
- {
- head = tail = cnt;
- pre[tail] = head;
- next[head] = tail;
- }
- else
- {
- pre[cnt] = tail;
- next[tail] = cnt;
- tail = cnt;
- }
- ++ size;
- }
- void push_front(long long x)
- {
- data[++cnt] = x;
- if(!size)
- {
- head = tail = cnt;
- pre[tail] = head;
- next[head] = tail;
- }
- else
- {
- pre[head] = cnt;
- next[cnt] = head;
- head = cnt;
- }
- ++ size;
- }
- long long pop_back()
- {
- long long re = data[tail];
- if(size > )
- {
- tail = pre[tail];
- next[tail] = ;
- }
- else
- head = tail = ;
- -- size;
- return re;
- }
- long long pop_front()
- {
- long long re = data[head];
- if(size > )
- {
- head = next[head];
- pre[head] = ;
- }
- else
- head = tail = ;
- -- size;
- return re;
- }
- }q[MAXN];
- //严格递减转严格递增,不用变负数
- long long ans1, ans2,n,num[MAXN],dp[MAXN],f[MAXN],g[MAXN],sum[MAXN];
- int main()
- {
- read(n);
- for(register long long i = n;i >= ;-- i) read(num[i]);
- memset(f, 0x3f, sizeof(f));
- for(register long long i = ;i <= n;++ i)
- {
- f[dp[i] = std::lower_bound(f + , f + + n, num[i]) - f] = num[i];
- ans1 = max(ans1, dp[i]);
- }
- //在每个f[i]下面挂一个队列,这个队列显然是单调不增的。
- //每访问到一个元素i,把所有f[i]-1所在队列中队首大于等于它的全部删除,并更新其方案数
- // 然后,将f[i]所在队列队尾跟它相等的删除
- register long long x = ;
- for(register long long i = ;i <= n;++ i)
- {
- if(dp[i] == )g[i] = ;
- else
- {
- x = dp[i] - ;
- while(q[x].size &&(num[data[q[x].head]] >= num[i]))sum[x] = (sum[x] - g[data[q[x].pop_front()]] + MOD) % MOD;
- g[i] = sum[x]%MOD;
- }
- x = dp[i];
- while(q[x].size && (num[data[q[x].tail]] == num[i]))sum[x] = (sum[x] - g[data[q[x].pop_back()]] + MOD)%MOD;
- q[x].push_back(i);
- sum[x] = (sum[x] + g[i]) % MOD;
- }
- long long tmp = ;
- for(register long long i = n;i >= ;-- i)
- if(dp[i] == ans1 && tmp != num[i])
- ans2 = (ans2 + g[i])%MOD, tmp = num[i];
- printf("%lld %lld", ans1, ans2%MOD);
- return ;
- }
T3
顺便给标称的代码
- #include<cstdio>
- #include<algorithm>
- #define M 300005
- #define mo 1000000007
- using namespace std;
- int n;
- int a[M],f[M],mi[M],g[M],sum[M],pre[M],sub[M];
- struct node{
- int siz,head,tail;
- int front(){return head;}
- int back(){return tail;}
- bool empty(){return siz==;}
- void push_back(int x)
- {
- if (siz)
- {
- int y=tail;
- pre[x]=y;
- sub[y]=x;
- }
- tail=x;
- if (siz==) head=x;
- ++siz;
- }
- void pop_back()
- {
- if (siz>)
- {
- int y=pre[tail];
- sub[y]=;
- tail=y;
- }
- else head=tail=;
- --siz;
- }
- void pop_front()
- {
- if (siz>)
- {
- int y=sub[head];
- pre[y]=;
- head=y;
- }
- else head=tail=;
- --siz;
- }
- }q[M];
- int in()
- {
- int t=;char ch=getchar();bool f=;
- while (ch<''||ch>'') f|=(ch=='-'),ch=getchar();
- while (ch>=''&&ch<='') t=t*+(ch^),ch=getchar();
- return f?-t:t;
- }
- int up(int x,int y)
- {
- x+=y;
- return x<?x+mo:(x>=mo?x-mo:x);
- }
- main()
- {
- freopen("data.txt","r",stdin);
- n=in();
- for (int i=;i<=n;++i) a[i]=in();
- int len=;
- for (int x,i=;i<=n;++i)
- {
- x=lower_bound(mi+,mi+len+,-a[i])-mi;
- mi[x]=-a[i];
- if (x>len) len=x;
- f[i]=x;
- }
- for(int x,i=;i<=n;++i)
- {
- if (f[i]==) g[i]=;
- else
- {
- x=f[i]-;
- while (!q[x].empty()&&a[q[x].front()]<=a[i]) sum[x]=up(sum[x],-g[q[x].front()]),q[x].pop_front();
- g[i]=sum[x];
- }
- x=f[i];
- while (!q[x].empty()&&a[q[x].back()]==a[i]) sum[x]=up(sum[x],-g[q[x].back()]),q[x].pop_back();
- q[x].push_back(i);
- sum[x]=up(sum[x],g[i]);
- }
- int t=1e9,ans=;
- for (int i=n;i;--i)
- if (f[i]==len&&t>a[i])
- ans=up(ans,g[i]),
- t=a[i];
- printf("%d %d\n",len,ans);
- }
zhn T3 std
NOIP模拟 17.8.18的更多相关文章
- NOIP模拟17.9.22
NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥
- NOIP模拟17.9.21
NOIP模拟17.9.21 3 58 145 201 161.5 样例输出21.6 数据规模及约定对于40% 的数据,N <= 20对于60% 的数据,N <= 1000对于100% 的数 ...
- NOIP模拟 17.8.20
NOIP模拟17.8.20 A.阶乘[题目描述]亲爱的xyx同学正在研究数学与阶乘的关系,但是他喜欢颓废,于是他就制作了一个和阶乘有关系的数学游戏:给出两个整数 n,m,令 t = !n,每轮游戏的流 ...
- NOIP模拟 17.8.15
NOIP模拟17.8.15 A 债务文件名 输入文件 输出文件 时间限制 空间限制debt.pas/c/cpp debt.in debt.out 1s 128MB[题目描述]小 G 有一群好朋友,他们 ...
- NOIP模拟 17.8.17
NOIP模拟17.8.17 A 小 G 的字符串文件名 输入文件 输出文件 时间限制 空间限制str.pas/c/cpp str.in str.out 1s 128MB[题目描述]有一天,小 L 给小 ...
- NOIP模拟 17.8.16
NOIP模拟17.8.16 A 债务文件名 输入文件 输出文件 时间限制 空间限制debt.pas/c/cpp debt.in debt.out 1s 128MB[题目描述]小 G 有一群好朋友,他们 ...
- NOIP模拟 17.8.14
NOIP模拟17.8.14 (天宇哥哥考察细心程度的题) [样例解释]如果删去第一个 1:在[3,1,2]中有 3 个不同的数如果删去 3:在[1,1,2]中有 2 个不同的数如果删去第二个 1:在[ ...
- NOIP模拟 17.9.28
公交车[问题描述]市内有
- Noip模拟17 2021.7.16
我愿称这场考试为STL专练 T1 世界线 巧妙使用$bitset$当作vis数组使用,内存不会炸,操作还方便,的确是极好的. 但是这个题如果不开一半的$bitset$是会炸内存的,因为他能开得很大,但 ...
随机推荐
- /etc/sysctl.conf配置文件
# vi /etc/sysctl.conf # add by digoal.zhou fs.aio-max-nr = fs. kernel.core_pattern= /data01/corefile ...
- python 日记 day4。
1.为何数据要分类 数据是用来表示状态的,不同的状态应该用不同类型的数据来表示. 2.数据类型 数字 字符串 列表 元组 字典 集合 列表:列表相比于字符串,不仅可以储存不同的数据类型,而且可以储存大 ...
- 深入浅出 Java Concurrency (2): 原子操作 part 1[转]
从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包,而Queue,很多情况下使用到了Atomic操作,因此首先从这里开始).很多情况下我们只是需要一个简单的 ...
- PAT甲级——A1043 Is It a Binary Search Tree
A Binary Search Tree (BST) is recursively defined as a binary tree which has the following propertie ...
- 通过java反射实现的excel数据导出
Excel.java @SuppressWarnings("deprecation") public static <T> void ExportExcel(Strin ...
- python-基础-函数-局部和全局变量
1 函数的定义和调用 1.1 函数定义 1.2 函数的调用 2 函数参数 >>> def fun(a, b, *args, **kwargs): ... ""&q ...
- Docker(六)安装Red5进行rtmp推流
1.pull镜像 docker pull mondain/red5 2.启动原版red5 docker run --name red5 -d -p 5080:5080 -p 1935:1935 mon ...
- Effective Modern C++ 条款4:掌握查看型别推导结果的方法
采用何种工具来查看型别推导结果,取决于你在软件开发过程的哪个阶段需要该信息.主要研究三个可能的阶段:撰写代码阶段.编译阶段.运行时阶段. IDE编译器 IDE中的代码编译器通常会在你将鼠标指针选停止某 ...
- Ubuntu 18.04 美化
Ubuntu 18.04 美化 sudo apt install gnome-tweak-tool sudo apt install gnome-shell-extensions sudo apt i ...
- HTTP协议①介绍
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准. HTTP是一个基于TCP/IP通信 ...