高端的N 校联考题
来源:互联网 发布:linux临时设置环境变量 编辑:程序博客网 时间:2024/03/29 16:18
序列
【问题描述】
作为一名火星人,你为了占领地球,需要想方设法使地球人失去信心。现在你获得了一项能力,控制今后n天的天气温度,对于第i天,你能将温度控制在[ai,bi]中任意一个数字,你的目的是使其中某段时间,温度持续不下降,趁此来攻击地球。现在问你最多可以使连续的多少天满足温度不下降。
【输入】 第一行给出一个整数n,表示你能控制的天数。 接下来n行,第i行给出2个整数ai,bi,表示你能控制的天气范围。保证ai<=bi。
【输出】 输出一个整数,表示答案。
【输入输出样例】
sequence.in
4
1 3
2 4
1 1
3 4
sequence.out
2
【数据范围】
对于20%的数据 3<=n<=10;
对于40%的数据 3<=n<=3000;
对于60%的数据 3<=n<=100000;
对于100%的数据 3<=n<=1000000,1<=ai,bi<=100000。
先以为是和如lis一样的简单dp
然后就那样写了 呵呵 出题人数据不卡这个 80……
因为如果从第一个开始可能并不一定是最优解
所以用这个思想 还要加上单调队列来维护
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;void init(){ freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);}inline 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*10+ch-'0';ch=getchar();} return x*f;}int n;int a[1000005],b[1000005];int ans=1;int q[1000005];int q_top=1,q_bot=0;int main(){ init(); n = read(); for(int i=1 ;i<=n ;i++) {a[i]=read();b[i]=read();} for(int i=1 ;i<=n ;i++) { while(q_top <= q_bot && a[q[q_top]] > b[i]) ++q_top; ans = max(ans , i - q[q_top-1]); while(q_top <= q_bot && a[i]>= a[q[q_bot]]) --q_bot; q[++q_bot] = i; } printf("%d",ans); return 0;}
循环整数
【问题描述】
moreD在学习完循环小数之后发现循环是个很美好的性质。自己只需要记住短短的循环节以及循环次数(次数大于1,且是整数)就可以记住整个数字了。
因为背诵数字变得方便了,moreD决定背诵[L,R]内的所有循环的整数。moreD的背诵计划有T天,但是他不知道每天具体要背多少个数,请你帮助moreD计算出每天需要背诵的数字个数。
如果moreD在某天遇到一个曾经背过的数字,他会义无反顾地重新背诵。
【输入格式】 第一行给出一个整数T,表示moreD计划背诵T天的数字。 接下来n行,第i行给出2个整数Li,Ri,表示moreD第i天的背诵计划。
【输出格式】 输出T行,每行一个整数,表示第i天moreD需要背诵的数字个数。
【输入输出样例】
circulate.in
3
1 10000
55555 66666
10 100
circulate.out
108
2
9
【数据范围】
对于30%的数据 T*MAX{Ri}<=2*10^6
对于70%的数据MAX{Ri}<=2*10^6
对于100%的数据 T<=50000,1<=Li<=Ri<=2*10^18
【样例解释】
对于第2天,moreD只需要背诵55555,66666.
对于第3天,moreD只需要背诵11,22,33,44,55,66,77,88,99.
我只知道我做了2个小时也没做出来
当然可以暴力其实那点小数据还是可以的
以下是题解
对于[L,R]内的循环整数个数,可以看成是[1,R]内的循环整数个数减去[1,L-1]内的循环整数个数。
{对于确定了循环节长度i以及数字长度n的循环整数,在[1,x]内的个数可以用MAX{x div k-10^(i-1)+1,0}算出,其中k是i-1个0,1个1,循环n div i次所得的数字。
例如,若求[1,666666]内长度为6的循环节长度为3的数字只需要用666666 div 1001-100+1=567.如此即可快速算出数字个数。这个公式成立当且仅当x的数字长度等于n。也就是说[1,666666]内长度为5的循环节长度为1的数字等于99999 div 11111-1+1=9.不能使用666666作为被除数.}(暴力枚举长度判断也可.}
所以就可以枚举循环节长度以及数字长度,利用以上公式算出循环整数的数字个数。
但是,很容易发现,这种做法会引起重复计算。因为如果一个整数的循环节长度是i,那只要k*i是数字长度的约数,k*i也是该整数的循环节长度,所以每次计算循环节长度为i的循环整数个数的时候也要把循环节长度为i的约数的所有合法循环整数减去。
也就是说,9999在循环节长度是1的时候已经算了一遍,而在循环节长度为2的时候也会算一次,所以要减去。
时间复杂度为O(T*(lgMAX{R})^2),期望得分100%。
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;void init(){ freopen("circulate.in","r",stdin); freopen("circulate.out","w",stdout);}int ans=0;inline long long read(){ long long 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*10+ch-'0';ch=getchar();} return x*f;}/*-----------------------------------------------------*/const int MaxNL = 20;long long tab[MaxNL];long long orz[MaxNL];//hard to think inline long long count(long long x){ int l = 0, d[MaxNL]; while (x > 0) d[++l] = x % 10, x /= 10;//!各!位数 long long ans = 0; for (int i = 1; i < l; ++i) ans += tab[i]; //满方案数 long long s = 1; for (int j = 1; j < l; ++j, s *= 10) if (l % j == 0) { long long t = 0; for (int i = l; i > l - j; --i) t = t * 10 + d[i]; long long p = 0; bool cutHand = false; for (int i = l - j; i > 0; --i) { p = p * 10 + d[i]; if (i % j == 1 || j == 1) { if (p < t) cutHand = true; else if (p > t) break; p = 0; } } orz[j] = t - s - cutHand + 1; for (int k = 1; k < j; ++k) if (j % k == 0) orz[j] -= orz[k]; ans += orz[j]; } return ans;}void prepare(){for (int i = 1; i < MaxNL; ++i) //位数 { long long s = 9; // 首位 for (int j = 1; j < i; ++j)//枚举约数 可循环位数 { if (i % j == 0) { orz[j] = s; for (int k = 1; k < j; ++k) if (j % k == 0) orz[j] -= orz[k]; //减去约数的约数 tab[i] += orz[j];//加上当前方案数 } s *= 10; } }}int T;long long l,r;int main(){ init(); prepare(); /* for(int i = 1 ;i<=18 ;i++)printf("%d ",tab[i]); printf("\n"); for(int i = 1 ;i<=18 ;i++)printf("%d ",orz[i]); /**/ T=read(); while(T--){ l=read();r=read(); long long ans = count(r) - count(l-1); printf("%lld\n",ans); } return 0;}
小Y的炮
【问题描述】
小Y最近开发出了批量制造大威力轰山炮的方法。才过去不到几个月,小Y就制造出了M门款式不同的轰山炮。第i门轰山炮发射一次,能使一座当前高度不高于Ai的山的高度降低Di(当然山的高度不能轰到0以下)。应政府要求,小Y要用他开发的轰山炮轰平开发区的几座山。由于开发区急需土地资源,政府要求小Y轰平尽量多的山(轰平:使山的高度降低至0)。
但是小Y制造的弹药有限,导致他最多只能发射K次。
小Y想知道,他最多能轰平几座山?轰平这些山后,弹药最多还够他发射几次?
【输入】
第一行三个正整数N,M,K,分别表示山的数目、轰山炮的款式数目、最多发射次数。
接下来N行,每行一个正整数Hi,表示第i座山的高度,输入数据保证Hi是降序的(从大到小)。
接下来M行,每行两个正整数Ai,Di,分别表示第i款轰山炮能轰的山的最高高度,和轰掉的山高度的减少值。
【输出】
一行两个整数Max,Remain,分别表示最多轰平的山的数目和轰平这些山后最多的剩余发射次数。
【输入输出样例】
cannon.in
3 2 3
8
6
2
10 6
6 5
cannon.out
2 1
【数据范围】
20%的数据满足N<=100,M<=100,Hi,Ai<=100。
50%的数据满足N<=1000,M<=500。
80%的数据满足N<=250000,M<=500。
20%、50%、80%的数据均满足Hi,Ai<=1000000。
100%的数据满足N<=250000,M<=500,K,Hi,Ai<=10^18,Di<=500。
贪心当然是一种优秀的策略
注意判断一些不必要的条件50还是可以的
当然正解不是这样的
以下
100%:注意到D的范围很小。假如一个高度H在区间i内,一直降低,在H刚好降低到刚好比A[i-1]小时,总有A[i-1]-H<=D。对于任何高度都有上述结论。所以我们最多只需处理O(M*D)个高度即可。令F[H]为轰平H高度的山的最少发射次数,则对于某个高度H,一直降至H-t*Di<=A[i-1],有F[H]=F[H-t*Di]+t,由于有用的H最多只有O(M*D)个,所以这样做的效率为O(M*D)。(F数组可由HASH代替)
那么对于从低到高的每座山,设该山高度为H,找到H对应的区间i(O(N+M)),将H降至H-t*Di<=A[i-1],那么最少发射次数为t+F[H-t*Di],其中H-t*Di必然是O(M*D)个高度中的一个。所以对于每座可以被轰的山,都可以用O(1)的时间得到轰平所需的最少发射次数。
不完全正确的代码
(不是我写的)………
因为我还没全对
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<map>using namespace std;typedef long long ll;void init(){ freopen("cannon.in","r",stdin); freopen("cannon.out","w",stdout);}const int maxn=250004;struct data{ ll a,d; bool operator <(const data &b)const{ return (a<b.a || (a==b.a && d<b.d)); }}cnn[maxn],p[maxn];ll k,h[maxn],ans=0;int n,m,cnt=0;map<ll,ll>f;inline ll get(){ char c;while(!isdigit(c=getchar())); ll v=c-48;while(isdigit(c=getchar()))v=v*10+c-48; return v;}int main(){ init(); n=get();m=get();k=get(); for(int i=n;i>=1;--i)h[i]=get(); for(int i=1;i<=m;++i)cnn[i].a=get(),cnn[i].d=get(); sort(cnn+1,cnn+1+m); for(int i=1;i<=m;++i){ while(cnt && p[cnt].d<=cnn[i].d)--cnt; p[++cnt]=cnn[i]; } ll dn=0;f[0]=0; for(int i=1;i<=cnt;++i){ if(i!=1)dn=p[i-1].a; ll l=max(dn,p[i].a-p[i].d); for(ll j=l+1;j<=p[i].a;++j){ ll t=(j-dn-1)/p[i].d+1; f[j]=f[max(0ll,j-(p[i].d*t))]+t; } } int j=1; dn=0; for(int i=1;i<=n;++i){ while(j<=cnt && p[j].a<h[i])++j; if(j>cnt)break; if(j!=1)dn=p[j-1].a; ll t=(h[i]-dn-1)/p[j].d+1; ll q=f[max(0ll,h[i]-(p[j].d*t))]+t; if(k>=q)k-=q,++ans;else break; } printf("%lld ",ans); printf("%lld\n",k);}
- 高端的N 校联考题
- N校联考 D2
- 久违的题解 多校联考
- Cpp环境【CQ-NOIP2016四校联考模拟题(一)P3】树上的询问
- 夸张的MBA联考
- 一次爆炸的联考
- 关于五校联考
- 【五校联考】集体照
- 11.7多校联考
- 【五校联考2day2】恐怖的奴隶主
- 【五校联考2day2】互相追逐的点
- 【五校联考2day2】WYF的盒子
- 【WC2017四校联考3】优美的树 题解
- 【WC2017四校联考5】B君的宴请 题解
- 2012-12-1多校联考题 保送
- 2012-12-1多校联考题 英语
- 2012-12-1多校联考题 最爱
- 高端人士的准则
- Windows远程到Linux
- AbstractList 抽象类 源代码
- Android源码分析-资源加载机制解析
- 一些知识点的初步理解_2(流形学习,ing...)
- 【日常学习】【数学】【众数】codevs4419 FFF团卧底的菊花题解
- 高端的N 校联考题
- C++ 宏定义动态加载、工厂模式
- C#程序员开发WinForm必须知道的 Window 消息大全
- UVALive 3635-Pie-二分
- 火星人
- qt5.5.1在Cortex-a8上移植记录
- 使用PullToRefresh实现下拉刷新和上拉加载
- hdu 2803 The MAX
- OpenCV函数速查