早上完全起不来了,9点被回来的舍友叫醒,orz
假设第0个格子有1粒麦子,第1个格子有p粒麦子,之后每一个格子放入前两个格子的小麦数之和的小麦,若第a个格子有x粒麦子,问是否存在p,若存在,问第b个格子有多少粒麦子,否则输出-1(给出a,x,p)
按照这样,得到数列 1 , p , p + 1 , 2 p + 1 , 3 p + 2 , 5 p + 3 , 8 p + 5 … … 1,p,p+1,2p+1,3p+2,5p+3,8p+5…… 1,p,p+1,2p+1,3p+2,5p+3,8p+5……,所以很容易发现系数以及常数项都是斐波那契数列,所以容易得出代码
#include
using namespace std;
long long a,x,b,f[21]={0,1};
int main(){
for (int i=2;i<=20;i++) f[i]=f[i-1]+f[i-2];
while (scanf("%lld%lld%lld",&a,&x,&b)==3){
if ((x-f[a-1])%f[a]) puts("-1");//不存在p粒麦子的情况
else{
int z=(x-f[a-1])/f[a];//找到p
printf("%lld\n",f[b]*z+f[b-1]);//输出答案
}
}
return 0;
}
每一次将所有的书划分为尽量少的连续部分,使得每一部分的书的高度都是单调下降,然后将其中所有不少于2 本书的区间全部翻转。重复执行以上操作,最后使得书的高度全部单调上升。问最少需要翻转多少次。
做第一次翻转,再求逆序对。
Reason:因为不能保证直接求逆序对是最优的,所以要模拟第一次翻转,then为什么要求逆序对,因为把单调上升连续子序列转变后,那么再翻转就是在中间翻转,所以直接求逆序对就行了。
#include
#include
#include
using namespace std;
int n,a[100001],b[100001]; long long ans;
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void merge(int l,int mid,int r){
int low=l,high=mid+1,x=0;
while (low<=mid&&high<=r){
if (a[low]<=a[high]) b[++x]=a[low++];
else b[++x]=a[high++],ans+=mid-low+1;//求逆序对
}
while (low<=mid) b[++x]=a[low++];
while (high<=r) b[++x]=a[high++];
for (int j=1;j<=x;j++) a[l+j-1]=b[j];
}
void mergesort(int l,int r){
if (l<r){
int mid=(l+r)/2;
mergesort(l,mid);
mergesort(mid+1,r);
merge(l,mid,r);
}
}
int main(){
n=in(); int j=1;
for (int i=1;i<=n;i++) a[i]=in();
for (int i=1;i<n;i++)
if (a[i]<a[i+1]){
if (i-j>=1) for (int k=j;k<=(j+i)>>1;k++) //翻转
a[k]^=a[i-k+j],a[i-k+j]^=a[k],a[k]^=a[i-k+j];
if (i-j>=1) ans++; j=i+1;
}
if (n-j>=1) for (int k=j;k<=(j+n)>>1;k++) //翻转
a[k]^=a[n-k+j],a[n-k+j]^=a[k],a[k]^=a[n-k+j];
if (n-j>=1) ans++; mergesort(1,n);//归并排序
return !printf("%lld",ans);
}
#JZOJ 3463 军训
学号为 1 − n 1-n 1−n的学生,把他们分班,必须按照学号顺序,问在满足所有班的欠扁最高值之和不得超过Limit下,女友数之和最多的班级答案最小,保证有解
二分女友数的答案,然后 d p dp dp, f [ i ] f[i] f[i]表示分到学号 i i i时的欠扁最高值之和,then再用队列维护当前的班级,当然还要用 S T L STL STL中高级的 m u l t i s e t multiset multiset红黑树,满足删除,插入,查询是否存在或相等。
为什么要这么做,因为当枚举 j ∣ 1 ≤ j ≤ i j|1\leq j\leq i j∣1≤j≤i时会相对应增加时间,所以可以用骚操作找出最优的 j j j
#include
#include
#include
using namespace std; multiset<int>ms;
int n,l,r,lrp,s[20001],rp[20001],f[20001],q[20001];
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int min(int a,int b){return (a<b)?a:b;}
int max(int a,int b){return (a>b)?a:b;}
bool check(int mid){
f[0]=0; int x,y,j=1,head=1,tail=1; ms.clear();
for (int i=1;i<=n;i++){
while (s[i]-s[j-1]>mid) j++;//找出合法的j
while (head<tail&&q[head]<j){//出队
if (head+1<tail)
ms.erase(ms.find(f[q[head]]+rp[q[head+1]]));//删除不合法
head++;
}
while (head<tail&&rp[q[tail-1]]<=rp[i]){//如果当前最大值不满足
if (head+1<tail&&ms.find(f[q[tail-2]]+rp[q[tail-1]])!=ms.end())
ms.erase(ms.find(f[q[tail-2]]+rp[q[tail-1]]));//出队
tail--;
}
q[tail++]=i; if (head+1<tail) ms.insert(f[q[tail-2]]+rp[i]);//找出新的可能
f[i]=f[j-1]+rp[q[head]]; if (!ms.empty()) f[i]=min(f[i],*ms.begin());//dp
}
return f[n]<=lrp;//在范围内
}
int main(){
n=in(); lrp=in(); int x;
for (int i=1;i<=n;i++) rp[i]=in(),x=in(),s[i]=s[i-1]+x,l=max(l,x); r=s[n];//l是最大的女友值,r是总和(分成一个班)
while (l<r){//二分(最大值最小)
int mid=(l+r)>>1;
if (check(mid)) r=mid; else l=mid+1;
}
return !printf("%d",l);
}
一个序列,删去 k k k个数字,使连续且相同的子序列最长。
用一个队列维护当前 k k k个数字,用 s s s存下队列中不同的数字,并用哈希统计答案
#include
#include
#include
#define p 200003
#define max(a,b) (a>b)?a:b
using namespace std;
int n,k,x,ans,hash[p],sash[p],s; queue<int>q;
int in(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
int locate(int x){
int pos=x%p,i=0;
while (i<p&&hash[(pos+i)%p]&&hash[(pos+i)%p]!=x) i++;
return (pos+i)%p;
}
int main(){
n=in(); k=in();
for (int i=1;i<=n;i++){
x=in(); int pos=locate(x); q.push(x);//进入队列
if (!sash[pos]){//该数字不存在
hash[pos]=x; s++;//加入
while (s>k+1){//只能有k个不同的数字
int pos1=locate(q.front()); q.pop();
sash[pos1]--; if (!sash[pos1]) hash[pos1]=0,s--;//删除
}
}
sash[pos]++; ans=max(ans,sash[pos]); //统计答案
}
return !printf("%d",ans);
}