T1
题意:数列递推式f[i]=f[i-1]+f[i-2],给定f[0]=1,f[a]=x,问是否有满足条件的f[1]。如果没有,输出"-1";如果有,输出f[b]的值。a,b<=20,保证满足条件的f[1]<=1e6
思路:差点以为要写高精度233333,推一下公式发现f[i]=f[0]*fib[i-2]+f[1]*fib[i-1](fib[i]表示斐波那契数列的第i项),预处理一下fib数组,直接O(1)算出f[1],判断是否有解,有解输出f[b],反正都是O(数据组数)的
代码:
#include<bits/stdc++.h>
using namespace std; long long k,a,b;
const long long fib[21]={1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946};
int main(){
freopen("kela.in","r",stdin);
freopen("kela.out","w",stdout);
long long x;
while(~scanf("%lld%lld%lld",&a,&k,&b)){
if(a>1){
x=(k-fib[a-2])/fib[a-1];
if(x<0||x*fib[a-1]!=k-fib[a-2]){
puts("-1");
continue;
}
}
else x=k;
printf("%lld\n",fib[b-1]*x+fib[b-2]);
}
return 0;
}
T2
题意:给定1~n的排列,每次将所有严格递减的区间翻转,每翻转一个区间算一个操作(4231→2413算两个操作),保证初始排列中减区间的长度都是偶数。问进行多少次操作后能使该序列变为升序。 数据范围:nlogn能过
思路:在第一次进行翻转的时候,会将每个严格递减的区间变为递增,此后的每次操作都只能去掉一个逆序对,于是答案就是【两个数不在同一个递减区间内的】逆序对个数加上初始减区间的个数
代码:
#include<cstdio>
#define MAXN 100005
using namespace std; int n;
int a[MAXN];
long long tree_array[MAXN];
long long ans=0;
int main(){
freopen("rest.in","r",stdin);
freopen("rest.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",a+i);
for(int j=a[i];j;j-=j&(-j)) ans+=tree_array[j];
if(a[i]==1) continue;
for(int j=1;j<=n;j+=j&(-j)) ++tree_array[j];
for(int j=a[i];j<=n;j+=j&(-j)) --tree_array[j];
}
// printf("ni = %lld\n",ans);
for(int i=1,j=2;i<=n;i=j,++j){
for(;a[j]<a[j-1]&&j<=n;++j);
if(j>i+1) ans-=1ll*(j-i)*(j-i-1)/2,++ans;
// printf("L = %d R = %d\n",i,j-1);
}
printf("%lld",ans);
return 0;
}
T3…………怎么似曾相识23333
题意:
HYSBZ开学了!今年HYSBZ有n个男生来上学,学号为1…n,每个学生都必须参加军训。在这种比较堕落的学校里,每个男生都会有Gi个女朋友,而且每个人都会有一个欠扁值Hi。学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求:
1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况。
2.每个学生必须要被分到某个班上。
3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值。所有班的欠扁值之和不得超过Limit。
4.每个班的女友指数定义为该班中所有同学的女友数量之和。在满足条件1、2、3的情况下,分班应使得女友指数最高的那个班的女友指数最小。
请你帮HYSBZ的教务处完成分班工作,并输出女友指数最高的班级的女友指数。
输入数据保证题目有解。
思路:这玩意儿怎么似曾相识啊x 二分答案check一下就好了x
长得很像某y姓jq学长搬运过的题(bzoj 4639),先考虑二分答案,于是可以变成那道题了,然后粘过来改一改就过了【x
对于yjq搬运的题啊……可以naive地n^2 dp
枚举每次增加一段的左右端点,八十分啊
正解是用线段树+two pointers 优化一下
如果f[j]能更新f[i],肯定有性质【j+1到i的所有数的和没有超过二分的那个答案】,然后j+1到i中的最大数就是这一段的代价,可以用单调队列维护最大值,然后从………………f[j]+转移代价里面找最小值就是f[i]。
(╯‵□′)╯︵┻━┻我不管了语死早反正就是那个意思……【对未来的flaze】你自己看代码哼
#include<bits/stdc++.h>
#define MAXN 20057
using namespace std;
int n,lim;
int a[MAXN];
int b[MAXN];
long long sum[MAXN];
long long f[MAXN];
long long dt1[MAXN<<2],dt2[MAXN<<2],tag[MAXN<<2];
void pushdown(int now){
tag[now<<1]=tag[now],tag[now<<1|1]=tag[now];
dt2[now<<1]=dt1[now<<1]+tag[now],dt2[now<<1|1]=dt1[now<<1|1]+tag[now];
tag[now]=0;
}
void modify(int now,int l,int r,int L,int R,long long v){
if(L<=l&&r<=R) return tag[now]=v,dt2[now]=dt1[now]+v,void();
int mid=(l+r)>>1;
if(tag[now]) pushdown(now);
if(L<=mid) modify(now<<1,l,mid,L,R,v);
if(mid<R) modify(now<<1|1,mid+1,r,L,R,v);
dt2[now]=min(dt2[now<<1],dt2[now<<1|1]);
}
void modify(int now,int l,int r,int pos,long long v){
if(l==r) return dt1[now]=v,void();
int mid=(l+r)>>1;
if(tag[now]) pushdown(now);
if(pos<=mid) modify(now<<1,l,mid,pos,v);
else modify(now<<1|1,mid+1,r,pos,v);
dt1[now]=min(dt1[now<<1],dt1[now<<1|1]);
}
long long inqry(int now,int l,int r,int L,int R){
if(L<=l&&r<=R) return dt2[now];
int mid=(l+r)>>1;
if(tag[now]) pushdown(now);
long long rtn=0x3f3f3f3f3f3f3f3f;
if(L<=mid) rtn=inqry(now<<1,l,mid,L,R);
if(mid<R) rtn=min(rtn,inqry(now<<1|1,mid+1,r,L,R));
return rtn;
}
int que[MAXN],head,tail;
bool check(long long limit){
head=1;
tail=0;
f[0]=0;
for(int i=1;i<=n;++i){
while(sum[i]-sum[head-1]>limit)
++head;//,printf("%lld %lld %d\n",sum[i],sum[head-1],que[head]-1);
while(a[i]>a[que[tail]]) --tail;
modify(1,0,n,que[tail],i-1,a[i]);
f[i]=inqry(1,0,n,head-1,i-1);
que[++tail]=i;
modify(1,0,n,i,f[i]);
}
return f[n]<=lim;
}
int main(){
freopen("training.in","r",stdin);
freopen("training.out","w",stdout);
scanf("%d%d",&n,&lim);
for(int i=1;i<=n;++i)
scanf("%d%d",a+i,b+i),sum[i]=sum[i-1]+b[i];
a[0]=0x3f3f3f3f,sum[0]=0;
int L=0,R=sum[n];
while(L^R){
int mid=(L+R)>>1;
if(check(mid)) R=mid;
else L=mid+1;
}
printf("%d",L);
return 0;
}