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的更多相关文章

  1. NOIP模拟17.9.22

    NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥

  2. NOIP模拟17.9.21

    NOIP模拟17.9.21 3 58 145 201 161.5 样例输出21.6 数据规模及约定对于40% 的数据,N <= 20对于60% 的数据,N <= 1000对于100% 的数 ...

  3. NOIP模拟 17.8.20

    NOIP模拟17.8.20 A.阶乘[题目描述]亲爱的xyx同学正在研究数学与阶乘的关系,但是他喜欢颓废,于是他就制作了一个和阶乘有关系的数学游戏:给出两个整数 n,m,令 t = !n,每轮游戏的流 ...

  4. NOIP模拟 17.8.15

    NOIP模拟17.8.15 A 债务文件名 输入文件 输出文件 时间限制 空间限制debt.pas/c/cpp debt.in debt.out 1s 128MB[题目描述]小 G 有一群好朋友,他们 ...

  5. NOIP模拟 17.8.17

    NOIP模拟17.8.17 A 小 G 的字符串文件名 输入文件 输出文件 时间限制 空间限制str.pas/c/cpp str.in str.out 1s 128MB[题目描述]有一天,小 L 给小 ...

  6. NOIP模拟 17.8.16

    NOIP模拟17.8.16 A 债务文件名 输入文件 输出文件 时间限制 空间限制debt.pas/c/cpp debt.in debt.out 1s 128MB[题目描述]小 G 有一群好朋友,他们 ...

  7. NOIP模拟 17.8.14

    NOIP模拟17.8.14 (天宇哥哥考察细心程度的题) [样例解释]如果删去第一个 1:在[3,1,2]中有 3 个不同的数如果删去 3:在[1,1,2]中有 2 个不同的数如果删去第二个 1:在[ ...

  8. NOIP模拟 17.9.28

    公交车[问题描述]市内有

  9. Noip模拟17 2021.7.16

    我愿称这场考试为STL专练 T1 世界线 巧妙使用$bitset$当作vis数组使用,内存不会炸,操作还方便,的确是极好的. 但是这个题如果不开一半的$bitset$是会炸内存的,因为他能开得很大,但 ...

随机推荐

  1. vue:element-ui输入框绑定回车事件

    参考: https://segmentfault.com/q/1010000011347642 https://weiku.co/article/297/ vue监听input输入框的回车事件:key ...

  2. 跟我一起实战美团网一之[nodemon] app crashed - waiting for file changes before starting...

    环境准备 第一步安装 npm install -g npx npx create-nuxt-app at-app 与事件相关的包我们再安装一次 npm install --update-binary ...

  3. 基于Skyline与ArcGIS Server的二三维联动功能实现

    基于Skyline与ArcGIS Server的二三维联动功能实现主要利用WEB技术.ArcGIS for JavaScript.Skyline 二次开发以及ArcGIS 10.1 桌面工具. 利用A ...

  4. gitlab merge request

    分支提了mr之后, 又有commit 不用重新提mr,mr中会自动更新 要保证项目下的.git目录中有hooks这个目录(如果是从github迁移到gitlab的项目, 可能没有这个目录, 导致mr不 ...

  5. PipeCAD Import IDF

    PipeCAD Import IDF eryar@163.com IDF/PCF文件是国际标准管道数据交换文件,主要用于isogen生成ISO图.目前主流设计软件都可以生成这些格式的文件,如PDMS. ...

  6. Java学习记录--ModelMapper的使用

    在项目中很多时候需要把Model和DTO两个模型类来回转换,保证Model对外是隐私的,同时类似密码之类的属性也能很好地避免暴露在外了. 那么ModelMapper就是为了方便转换而实现的一个类库,下 ...

  7. Python3实用编程技巧进阶

    Python3实用编程技巧进阶  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时候可以 ...

  8. 一、初识asyncio协程

    初识asyncio协程 一.基本概念 ​ 要想了解学习协程相关知识要先对以下几个概念先行了解: 阻塞 ​ 阻塞状态是指程序未得到某所需计算资源时的挂起状态,简单说就是程序在等待某个操作未执行完前无法执 ...

  9. Luogu P2066 机器分配(dp)

    P2066 机器分配 题面 题目背景 无 题目描述 总公司拥有高效设备 \(M\) 台,准备分给下属的 \(N\) 个分公司.各分公司若获得这些设备,可以为国家提供一定的盈利.问:如何分配这 \(M\ ...

  10. Gradle:gradle下载插件

    https://github.com/michel-kraemer/gradle-download-task 用法在readme中已经讲的很清楚了,我主要介绍下注意事项吧. 我用这个插件的目的是为了让 ...