9.13 Test——NOIP模拟赛
作者:互联网
T1:backpack
NYG的背包
【问题描述】
NYG有一个神奇的背包,每放进去一个物品,背包的体积就会变大。
也就是说,每放进一个物品,背包会被占用一定的体积,但是紧接着背包的总体积又 会增大一定的值(注意是在放入物品后背包总体积才增大)。
NYG发觉这个背包十分好用,于是不由自主地想到了一个问题。
现在给出背包初始容量V 以及n个物品,每一个物品两个值a, b,分别表示物品所占体积 和放入背包后背包增大的体积。
NYG想知道能否把所有物品装进去? 因为NYG比较老实,这么简单的问题自然装作不会做的样子。 于是他来请教你。
【输入格式】
从文件backpack.in中读入数据
输入文件包含多组数据。
第一行一个数扔表示数据组数。
对于每组数据,第一行两个数n, h,接下来扮行每行两个数a, b表示物品所占体积和放入背包后背包增大的体积。
【输出格式】
输出到文件backpack.out中
对于每一组数据,如果能把所有物品装进去,输出"Yes",否则输出"No"(不包含引号)
【样例输入1】
3 5
3 1
4 8
8 3
【样例输出1】
Yes
【样例输入2】
3
7 9269
21366 1233
7178 23155
16679 23729
15062 28427
939 6782
24224 9306
22778 13606
5 22367
17444 5442
16452 30236
14893 24220
31511 13634
4380 29422
7 18700
25935 4589
24962 9571
26897 14982
20822 2380
21103 12648
32006 22912
23367 20674
【样例输出2】
Yes
Yes
No
【数据规模和约定】
对于1~4个测试点,T=10, 1≤n≤9, 1≤V≤100000, 0≤a,b≤100000
对于5∼8个测试点,T=20, 1≤n≤1000, 1≤V≤100000, 0≤a,b≤100000
对于9∼12个测试点,T=5, 1≤n≤100000, 1≤V≤1000, 0≤a,b≤1000
对于13~16个测试点,T=500, 1≤n≤1000, 1≤V≤100000, 0≤a,b≤100000
对于17∼20个测试点,T=8, 1≤n≤50000, 1≤V≤100000, 0≤a,b≤100000
解析:
$Maxmercer$上课时讲的例题,记得当时我和周围的人讨论了很久,下课我还找他问过一下的来着
思路显然是贪心
肯定需要把$a<b$的先用了,才可能搞得定$a>b$的
对于$a<b$的部分,肯定是先打$a$较小的,这样才可以尽力避免出现负数的情况
问题在于$a \leqslant b$的部分应该怎样安排,这也是当时我们上课时讨论的中心
这一部分思路很巧妙
反向地看这个放东西的部分,相当于是所需空间为$b$,能增大$a$的容量,$a>b$,这样的话就变成了和前面的情况了
因此这一部分只需要按$b$从大到小排序,再顺次扫过去就可以了
代码:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn = 100004; inline int read() { int ret, f=1; char c; while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1; ret=c-'0'; while((c=getchar())&&(c>='0'&&c<='9'))ret = (ret<<3)+(ret<<1)+c-'0'; return ret*f; } int T, n, s, cnt1, cnt2; struct thi{ ll a, b; }t[maxn], f[maxn]; bool cmp1(thi x, thi y) { return x.a < y.a; } bool cmp2(thi x, thi y) { return x.b > y.b; } int main() { freopen("backpack.in", "r", stdin); freopen("backpack.out", "w", stdout); T = read(); while(T--) { n = read();s = read(); cnt1 = cnt2 = 0; for(int i = 1; i <= n; ++i) { ll x = 1LL * read(), y = 1LL * read(); if(y > x) t[++cnt1] = (thi){x, y}; else f[++cnt2] = (thi){x, y}; } sort(t + 1, t + cnt1 + 1, cmp1); ll now = 1LL * s; bool fl = 0; for(int i = 1; i <= cnt1; ++i) { now -= t[i].a; if(now < 0) { fl = 1; break; } now += t[i].b; } if(!fl) { sort(f + 1, f + cnt2 + 1, cmp2); for(int i = 1; i <= cnt2; ++i) { now -= f[i].a; if(now < 0) { fl = 1; break; } now += f[i].b; } } printf("%s\n", fl? "No": "Yes"); } return 0; }backpack
T2:point
NYG的动态数点
【问题描述】
NYG是一个善于思考的好学生。
于是NYG想往他的背包中放序列。
某一天NYG得到了一个长度为n的序列{ai}。然后NYG说,如果对于一段区间[L, R],存在L ≤ k ≤ R使得∀i∈[L, R]都有ak | ai,我们认为它有价值,价值为R − L(若不满足条件则没有价值)。
现在NYG想知道所有区间中价值最大为多少,最大价值的区间有多少个,以及这些区间分别是什么。
【输入格式】
从文件point.in中读入数据
第一行一个整数n。
第二行n个整数ai。
【输出格式】
输出到文件point.out中
第一行两个整数,num和val,分别表示价值最大的区间的个数以及最大价值。
第二行num个整数,按升序输出每一个最大价值区间的L。
【样例输入1】
5
4 6 9 3 6
【样例输出1】
1 3
2
【样例输入2】
30
15 15 3 30 9 30 27 11 5 15 20 10 25 20 30 15 30 15 25 5 10 20 7 7 16 2 7 7 28 7
【样例输出2】
1 13
9
【数据规模和约定】
对于30%的数据,1≤n≤30, 1≤ai≤32
对于60%的数据,1≤n≤3000, 1≤ai≤1024
对于80%的数据,1≤n≤300000, 1≤ai≤1048576
对于100%的数据,1≤n≤500000, 1≤ai<232
【NYG教你学数学】
题意中这一句话:
存在L≤k≤R使得∀i∈[L,R]都有ak | ai。
的含义是:
在L到R中至少有一个位置k,满足对于L到R中的所有位置i,都有ak整除ai 。
解析:
读完一遍,发现可以二分答案
$check$的时候枚举左端点,如果用线段树维护$gcd$的话,时间复杂度就是$O(Nlog^{3}N)$,显然吃不消
考虑用$st$表维护出$gcd$和$min$,这样查询就是$O(NlogN)$的,总时间复杂度是$O(Nlog^{2})$的,就可过了
一开始的时候我还想多了,以为查询一段区间的$gcd$必须把区间拆分成几个不相交的区间,结果发现和普通的$st$表一样,给首位两段预处理好的$gcd$再取一次$gcd$就行了
代码:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn = 500004; template<class T> inline void read(T &ret) { T f=1; char c; while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1; ret=c-'0'; while((c=getchar())&&(c>='0'&&c<='9'))ret = (ret<<3)+(ret<<1)+c-'0'; ret *= f; } int n, lg[maxn], ans[maxn], val, cnt; ll a[maxn]; ll gc[25][maxn], mn[25][maxn]; struct pr{ ll fir, sec; }; inline ll gcd(ll x, ll y) { return y == 0? x: gcd(y, x % y); } inline pr getpr(int x, int len) { pr ret; ret.fir = gcd(gc[lg[len]][x], gc[lg[len]][x+len-(1<<lg[len])]); ret.sec = min(mn[lg[len]][x], mn[lg[len]][x+len-(1<<lg[len])]); return ret; } inline bool check(int x) { pr y; for(register int i = 1; i <= n - x + 1; ++i) { y = getpr(i, x); if(y.fir == y.sec) return 1; } return 0; } void work() { pr y; for(register int i = 1; i <= n - val + 1; ++i) { y = getpr(i, val); if(y.fir == y.sec) ans[++cnt] = i; } } int main() { freopen("point.in", "r", stdin); freopen("point.out", "w", stdout); read(n); lg[0] = -1; for(register int i = 1; i <= n; ++i) { read(a[i]); lg[i] = lg[i>>1] + 1; gc[0][i] = mn[0][i] = a[i]; } for(register int j = 1; j <= lg[n]; ++j) for(register int i = 1; i <= n - (1<<j) + 1; ++i) { gc[j][i] = gcd(gc[j-1][i], gc[j-1][i+(1<<(j-1))]); mn[j][i] = min(mn[j-1][i], mn[j-1][i+(1<<(j-1))]); } register int l = 1, r = n, mid; while(l <= r) { mid = (l + r)>>1; if(check(mid)) val = mid, l = mid + 1; else r = mid - 1; } work(); printf("%d %d\n", cnt, val - 1); for(register int i = 1; i <= cnt; ++i) printf("%d ", ans[i]); return 0; }point
T3:excellent
NYG的序列拆分
【问题描述】
因为NYG很老实,于是他又对放入背包的序列开始了思考。
由于NYG很擅长序列问题,他的一大爱好就是对这些序列进行拆分。
一个长为n的正整数序列A,对1≤i≤n都有Ai∈[l, r],NYG定义它的一个拆分为一个长为k的序列S,满足:
1. S1 = 1
2. Sk = n +1
3. Si < Si+1,1≤i<k
NYG认为,一个拆分是优秀(高贵)的,当且仅当对于每一个i, 1≤i<k,A中的元素$A_{S _{i}}$, $A_{S _{i}+1}$ . . . , $A_{S _{i+1}-1}$构成等比数列。
给出n, l, r, NYG需要你求出所有可能的(r − l +1)n个序列的优秀拆分的个数总和。由于答案可能很大,输出对109+7取模。
NYG觉得这样的拆分实在是太多了,所以就把任务扔给了你。
【输入格式】
从文件excellent.in中读入数据
第一行一个整数T, 表示数据组数。
接下来扔行每行三个整数n, l, r, 意义如上所述。
【输出格式】
输出到文件excellent.out中
输出共T行,每行一个整数表示答案。
【样例输入1】
4
1 1 2
10 6 6
3 1 4
100 1000 100000
【样例输出1】
2
512
198
540522901
【数据规模和约定】
保证T≤10, 1≤l≤r≤107, 1≤n≤1018。
我们用$\sum$
标签:...,cnt,NOIP,int,9.13,bmatrix,Test,NYG,dp 来源: https://www.cnblogs.com/Joker-Yza/p/11518001.html