一道经典题目了。以前就作过(2019年山东省夏令营,2019-07-20 09:14,看来是在礼堂听课时作的)
闲话很少说就是单调队列,一种数据结构,维护两个方面:
c++
为了懒,固然用stl:deque啦(实际上是head==tail仍是head>tail搞不清楚)
git
不过用st表也能AC,须要注意卡常数,代码以下:算法
#include<cstdio> #include<cmath> #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) int n,k,a[1000006],st[1000006][22]; inline int read()//不快读的话过得很勉强,快读就比较轻松。另外不要引用isdigit()函数,会变慢 { int x=0,f=1;char ch=getchar(); while (ch>'9'||ch<'0'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f; } int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;++i){ //scanf("%d",a+i); st[i][0]=read(); } for(int j=1,maxx;j<=20;++j){ maxx=n-(1<<j)+1; for(int i=1;i<=maxx;++i) st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]); } int len=log2(k);//直接用此函数的话,已试验,输入整数时无偏差 for(int i=1;i<=n-k+1;++i) printf("%d ",min(st[i][len],st[i+k-(1<<len)][len]));//实际上是(i+k-1)-(1<<len)+1 printf("\n"); for(int j=1,maxx;j<=20;++j){ maxx=n-(1<<j)+1; for(int i=1;i<=maxx;++i) st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]); } for(int i=1;i<=n-k+1;++i) printf("%d ",max(st[i][len],st[i+k-(1<<len)][len])); return 0; }
接下来是蚯蚓这道题数组
/** * 蛐蛐国里如今共有 n 只蚯蚓( n 为正整数)。 * 每只蚯蚓拥有长度,咱们设第 i 只蚯蚓的长度为 a[i] * (i=1,2,...,n) * 并保证全部的长度都是非负整数 * (即:可能存在长度为 0 的蚯蚓)。 *//** * 每一秒,神刀手会在全部的蚯蚓中, * 准确地找到最长的那一只(若有多个则任选一个) * 将其切成两半。 * 神刀手切开蚯蚓的位置由常数 p * (是知足 0 < p < 1 的有理数) * 决定,设这只蚯蚓长度为 x, * 神刀手会将其切成两只长度分别为 * ⌊px⌋ 和 x - ⌊px⌋ 的蚯蚓。 * 特殊地,若是这两个数的其中一个等于 0, * 则这个长度为 0 的蚯蚓也会被保留。 * 此外,除了刚刚产生的两只新蚯蚓, * 其他蚯蚓的长度都会增长 q(是一个非负整常数)。 */
附上ckw大佬的分析(也是2019年夏令营的)
数据结构
那么思想的话已经很明显了。正解也不少,文章最后贴一个正解作法(咱们教练写的)。这里给一个骗分的作法:函数
咱们快速写一个不是正解可是必定对的代码而后疯狂卡常,以下:
考虑到每次取最大蚯蚓,且不断插入/删除,咱们用一个优先队列来维护
其余的就是照着题面模拟了。须要注意的是每次插入新的蚯蚓时要减去time×q以“拉平”,
换言之,其余蚯蚓都少加了time×q,切的这条蚯蚓切的时候是真实的长度,切后要变回“虚假”的长度
其余的按照体面输入输出就完了。开不开O2优化都是85分,优化做用也有限。优化
#include<cstdio> #include<queue> using namespace std; int _X,_F,CH; inline int read(){//read _X=0;_F=1;CH=getchar(); while(CH>57||CH<48){if(CH=='-')_F=-1;CH=getchar();} while(CH>=48&&CH<=57){_X=_X*10+CH-48;CH=getchar();} return _X*_F; } priority_queue<int/*,vector<int>,greater<int> */>pq; int n,m,q,u,v,t/*,a[100005]*/;//含义见题目 int longest,shorter;//变量名起的长一点便于理解 int main(){ n=read();m=read();q=read();u=read();v=read();t=read(); for(int i=1;i<=n;++i) { //a[i]=read(); pq.push(read()); } for(int time=1;time<=m;++time) { longest=pq.top();pq.pop(); longest+=(time-1)*q;//本轮他并无变长 if(time%t==0)printf("%d ",longest); shorter=longest*(long long)u/v;//这个long long常数很大,可是必须 pq.push(shorter-time*q);pq.push(longest-shorter-time*q); //写这篇文章时我想到了把time*q存起来,可是该拿的85分到手了,剩下几个点真的没什么用 } putchar('\n'); int addlen=m*q;//<2^31 for(int i=1;i<=n+m;++i) { if(i%t){pq.pop();continue;} longest=pq.top();pq.pop(); printf("%d ",longest+addlen); } return 0; }
这是一份正解,三个单调队列,含义如ckw所述。题解满大街都是,贴个咱们教练的:spa
# include<bits/stdc++.h> #define rep(i,n) for(int i=1;i<=n;++i) using namespace std; inline int read() { int x=0,f=1; char ch;ch=getchar(); while(ch<48 ||ch>57){if(ch=='-') f=-1;ch=getchar(); } while(ch>=48&&ch<=57) {x=x*10+ch-48;ch=getchar();} return x*f; } const int N=1e5+5; const int M=1e7; int a[3][M],cut[M],h[3],t[3];//a[0] 原数列 a[1] 砍断后长的段 a[2]砍断后短的段 // cut [] 记录被砍断的蚯蚓 h[0] h[1] h[2] 表明 a[0] a[1] a[2]头指针 t[]分别是尾指针 int n,m,q,u,v,T,inf; bool cmp(int &a,int &b) {return a>b;} int main() { //freopen("in.txt","r",stdin); cin>>n>>m>>q>>u>>v>>T; for(int i=0;i<=2;++i) rep(j,M)a[i][j]=-1e9; inf=-1e9; h[0]=h[1]=h[2]=1; cut[0]=0; rep(i,n) a[0][i]=read(); sort(a[0]+1,a[0]+1+n,cmp);// 首先让a[0] 从大到小 有序 double per=u*1.0/v; rep(i,m)// 开始砍蚯蚓 { int temp,maxn; if(a[0][h[0]]>=a[1][h[1]]) maxn=a[0][h[0]],temp=0; else maxn=a[1][h[1]],temp=1; if(a[2][h[2]]>maxn) maxn=a[2][h[2]],temp=2; // 找到a[0] a[1] a[2] 队首最大的 就是 应该被砍的蚯蚓 h[temp]++;//被砍的蚯蚓被弹出队首 if(i%T==0)cut[++cut[0]]=maxn+(i-1)*q;//被砍的蚯蚓应当加上增加补偿,每一秒都增加q // 接下来把砍断的蚯蚓 按大小存在a[1] a[2] 队列 int small=(maxn+(i-1)*q)*per,big=maxn+(i-1)*q-small; if(big<small) swap(big,small); a[1][++t[1]]=big-i*q,a[2][++t[2]]=small-i*q; } //按要求输出 过后我想cut数组也许没有必要 直接输出便可 rep(i,cut[0]) cout<<cut[i]<<" "; cout<<endl; int left=(n+m)/T,cnt=0; // 后来身负洪荒之力人来了 蚯蚓不在增加 只须要把序列输出便可 while(left) { int temp,maxn=inf; if(a[0][h[0]]>=a[1][h[1]]) maxn=a[0][h[0]],temp=0; else maxn=a[1][h[1]],temp=1; if(a[2][h[2]]>=maxn) maxn=a[2][h[2]],temp=2; h[temp]++; if(++cnt%T==0)cout<<maxn+m*q<<" ",left--; } return 0; }
还有一位同窗因常数过大被卡因此必定快读:
指针
文末总结:
一些看似高级的题目其实可能有简单(吗?)的作法,尤为是单调队列,好几回出现了
因此仍是那句话,基础算法灵活掌握。
另外就是ckw教的经验吧,先考虑部分分的限制条件,不行就先本身加一个,在这种受限的背景下题目还会含有什么限制条件,
再推广到更高分看是否是还成立。我以为部分分就挺香的了code