zoukankan      html  css  js  c++  java
  • 18.10.30绍一模拟赛

    T1斐波那契

    题意

    给定一个长度为n的数列,第i个数为(a_i),要求给数列划分。
    要求一个块内任意两个数之和不在斐波那契数列上。
    【样例输入】
    5
    1 5 2 6 1 5 2 6 7
    【样例输出】
    4
    【样例说明】
    最优分组的一种为: 最优分组的一种为: 最优分组的一种为: 最优分组的一种为: 最优分组的一种为: 最优分组的一种为: {1}, {5, 2}, {6}, {7}。
    【数据范围】
    对于 10% 的数据,(n ≤ 20)
    对于 30% 的数据,(n ≤ 300)
    对于 60% 的数据,(n ≤ 1000)
    对于 100 % 的数据,(n ≤ 100000, a_i ≤ 10^9)

    分析

    (2 imes a_ile 2*10^9)别看这个范围很大,但是斐波那契数列第47项就比这个数大了,打表!!
    既然这么小,用一个桶记录每一个数是不是为斐波那契数列上的数就不划算了,我们直接暴力查询(二分也行)就可以了。
    我考场上的思路是dp,(g[i])表示第i个点最远向左扩展到(g[i])位置不合法。
    我们可以很容易预处理出这个,用一个map(据说会被卡),或者set,或者手打哈希表储存前(1~i-1)个数,然后枚举每一个(fib[j]-i),查询是否存在,取最近的一个位置。
    然后再和(g[i-1])求一下最大值就可以了。
    然后考虑dp,(f[i])表示(1~i)最小分多少组。
    很明显(f[i]=min{f[i-k]+1,g[i]le i-kle i-1})
    然后发现是(O(n^2))的,过不去,可以用单调队列优化,均摊一个n,轻松能过。
    不知道为什么会被卡一个点。

    标算是贪心。
    因为很明显,位置越多分组肯定越多,我们不断加入一个数,判断它跟最近的组是不是有冲突。
    如果有冲突我们就新开一组,否则加入上一组。
    用哈希表可以做到(O(1))查询,这样子我们就能(O(n))解决问题了。

    代码

    dp代码(90pts)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <map>
    #define ll long long
    using namespace std;
    const int Maxn=100009;
    ll read(){
    	char c;ll num,f=1;
    	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    	while(c=getchar(), isdigit(c))num=num*10+c-'0';
    	return f*num;
    }
    ll fib[Maxn],a[Maxn],g[Maxn];
    ll f[Maxn],d[Maxn],q[Maxn],n,h=1,t=0;
    map<int,int> m;
    bool ask(ll a);
    void init();
    int main()
    {
    	freopen("f.in","r",stdin);
    	freopen("f.out","w",stdout);
    	init();
    	for(int i=1;i<=n;i++){
    		while(d[h]<g[i])h++;
    		f[i]=q[h]+1;
    		while(f[i]<=q[t])t--;
    		q[++t]=f[i];d[t]=i;
    	}	
    	cout<<f[n]<<endl;
    	return 0;
    }
    bool ask(ll a){
    	int l=1,r=49,mid;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(fib[mid]==a)return 1;
    		if(fib[mid]<a)l=mid+1;
    		else r=mid-1;
    	}
    	return 0;
    }
    void init(){
    	fib[1]=1;fib[2]=1;n=read();
    	for(int i=3;i<=48;i++)fib[i]=fib[i-1]+fib[i-2];
    	for(int i=1;i<=n;i++)a[i]=read();g[1]=0;m[a[1]]=1;
    	for(int i=2;i<=n;i++){
    		int pos=0;
    		for(int j=1;j<=48;j++)
    			if(fib[j]-a[i]>=1&&m.find(fib[j]-a[i])!=m.end())
    				pos=max(pos,m[fib[j]-a[i]]);
    		g[i]=pos;
    		m[a[i]]=i;
    		g[i]=max(g[i-1],g[i]);
    	}
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;q[++t]=f[0];d[t]=0;
    }
    /* 对每一只兔子枚举它之前第一个和他组成兔子数列的兔子
     * 然后跑一遍dp,求出最小划分数。
     * g[i]表示从i开始,最大向左拓展的长度。
     * 发现g[i]是递增的,我们可以考虑单调队列优化。 
     * 但是被预处理卡了。。
     */ 
    

    贪心代码(AC)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #define ll long long
    using namespace std;
    const int mod=133531;
    const int Maxn=100009;
    int read(){
    	char c;int num,f=1;
    	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    	while(c=getchar(), isdigit(c))num=num*10+c-'0';
    	return f*num;
    }
    ll n,fib[Maxn],a[Maxn],ans=0;
    ll head[mod+100],nxt[mod+100],val[mod+100],tot=1;
    bool Find(int x){
    	int a=(x%mod+mod)%mod;
    	for(int i=head[a];i;i=nxt[i])
    		if(val[i]==x)return 1;
    	return 0;
    }
    void Clean(){
    	memset(head,0,sizeof(head));
    	tot=1;
    }
    void Insert(int x){
    	int a=(x%mod+mod)%mod;
    	val[++tot]=x;
    	nxt[tot]=head[a];
    	head[a]=tot;
    }
    int main()
    {
    	freopen("f.in","r",stdin);
    	freopen("f.out","w",stdout);
    	fib[1]=1;fib[2]=1;n=read();
    	for(int i=3;i<=48;i++)fib[i]=fib[i-1]+fib[i-2];
    	for(int i=1;i<=n;i++)a[i]=read();
    	Clean();Insert(a[1]);ans=1;
    	for(int i=2;i<=n;i++){
    		for(int j=1;j<=47;j++){
    			if(fib[j]-a[i]>=1&&Find(fib[j]-a[i])){
    				ans++;
    				//cout<<i<<endl;
    				Clean();
    				break;
    			}
    		}
    		Insert(a[i]);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    

    T2好数

    题意

    给定一堆素数(每个都有无数个),用这些素数相乘,要求结果小于R。
    求方案数以及最大值。
    【样例输入】
    3 30
    2 3 7
    【样例输出】
    28
    16
    【数据范围】
    对于 30% 30% 30% 30% 的数据, 的数据,$ k ≤ 10 ,R ≤ 1000000 (。 对于 60% 60% 60% 60% 的数据, 的数据,) k ≤25 ,R ≤ 10^12 (。 对于 100% 100% 100% 100% 100% 的数据, 的数据, 的数据,) k ≤ 25 ,R ≤ 10^18 ,p_i ≤ 100 $。

    分析

    打完暴力发现60pts的时候最大也才(10^6)的答案。
    爆搜就可以拿到60分。
    然后100pts时答案在(10^12)左右。
    我们可以考虑meet in mid。
    把数据分成两部分,分别进行爆搜,统计出两个集合,那么还要统计两个集合的对应乘积。
    我们对两个个集合进行排序,我们可以双指针统计出乘积小于R的方案数。

    代码

    暴力(60pts)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #define ll long long
    using namespace std;
    ll read(){
    	char c;ll num,f=1;
    	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    	while(c=getchar(), isdigit(c))num=num*10+c-'0';
    	return f*num;
    }
    ll k,r,p[109],now=1,ans=0,maxn;
    void dfs(int d){
    	if(d>k){
    		ans++;
    		maxn=max(maxn,now);
    		return ;
    	}
    	ll qwq=now;
    	while(1){
    		if(now<r)dfs(d+1);
    		else break;
    		now*=p[d];
    	}
    	now=qwq;
    }
    int main()
    {
    	freopen("h.in","r",stdin);
    	freopen("h.out","w",stdout);
    	k=read();r=read();
    	for(int i=1;i<=k;i++)p[i]=read();
    	dfs(1);
    	printf("%lld
    %lld
    ",maxn,ans);
    	return 0;
    }
    

    100pts

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #define ll long long
    using namespace std;
    ll read(){
    	char c;ll num,f=1;
    	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    	while(c=getchar(), isdigit(c))num=num*10+c-'0';
    	return f*num;
    }
    ll k,k1,k2,r,p[109],p1[109],p2[109],now=1,ans=0,maxn;
    ll a1[14000005],a2[14000005],cnt1=0,cnt2=0;
    void dfs1(int d,ll v){
    	if(d>k1){
    		a1[++cnt1]=v;
    		maxn=max(maxn,v);
    		return ;
    	}
    	//cout<<v<<endl;
    	ll t1=r/p1[d],t2=r/v,res=1;
    	while(1){
    		if(res>t2) break;
    		dfs1(d+1,v*res);
    		if(res>t1) break;
    		res=res*p1[d];
    	}
    }
    void dfs2(int d,ll v){
    	if(d>k2){
    		a2[++cnt2]=v;
    		maxn=max(maxn,v);
    		return ;
    	}
    	//cout<<v<<endl;
    	ll t1=r/p2[d],t2=r/v,res=1;
    	while(1){
    		if(res>t2) break;
    		dfs2(d+1,v*res);
    		if(res>t1) break;
    		res=res*p2[d];
    	}
    }
    bool cmp(ll a,ll b){return a<b;}
    int main()
    {
    	freopen("h.in","r",stdin);
    	freopen("h.out","w",stdout);
    	k=read();r=read();k1=0;k2=0;
    	for(int i=1;i<=k;i++)p[i]=read();sort(p+1,p+1+k,cmp);
    	for(int i=1;i<=k;i++){
    		if(i<=8)p1[++k1]=p[i];
    		else p2[++k2]=p[i];
    	}
    	dfs1(1,1);
    	dfs2(1,1);//cout<<1<<endl;
    	sort(a1+1,a1+1+cnt1,cmp);
    	sort(a2+1,a2+1+cnt2,cmp);
    	ll t1=1,t2=cnt2;
    	//for(int i=1;i<=cnt1;i++)cout<<a1[i]<<" ";cout<<endl;
    	//for(int i=1;i<=cnt2;i++)cout<<a2[i]<<" ";cout<<endl;
    	
    	while(t1<=cnt1){
    		while((t2 >= 1) && (((long double)a1[t1] * a2[t2] > 2*r) || (a1[t1] * a2[t2] > r))){
    			t2--;
    		}if(!t2) break;
    		maxn = max(maxn,a1[t1] * a2[t2]);
    		ans += t2;
    		t1++;
    	}
    	printf("%lld
    ",maxn);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    T3序列

    题意

    给定一个序列,求在序列中中位数在(l_1)(r_1)之间并且满足长度在(l_2~r_2)之间的序列的个数。

    分析

    我怎么可能会写啊。。
    打了个暴力滚粗了。
    正解好像是线段树。。

    代码

    40pts

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <string>
    #define ll long long
    using namespace std;
    const int Maxn=100009;
    int read(){
    	char c;int num,f=1;
    	while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    	while(c=getchar(), isdigit(c))num=num*10+c-'0';
    	return f*num;
    }
    int n,m,l1,l2,r1,r2,maxn,flag;
    int a[Maxn*2],f[Maxn*2],len;
    ll ans;
    void ask();
    int main()
    {
    	freopen("x.in","r",stdin);
    	freopen("x.out","w",stdout);
    	n=read();for(int i=1;i<=n;i++)a[i]=read(),maxn=max(a[i],maxn);
    	m=read();
    	while(m--){
    		l1=read();r1=read();
    		l2=read();r2=read();
    		ans=0;ask();
    	}
    	return 0;
    }
    void ask(){
    	l1=max(l1,1);r1=min(r1,maxn);
    	l2=max(1,l2);r2=min(n,r2);
    	for(int i=1;i<=n;i++)
    		if(a[i]>r1)f[i]=1;
    		else if(a[i]<l1)f[i]=-1;
    		else f[i]=0;
    	int s1,s2,s3;
    	for(int i=1;i<=n;i++){
    		s1=0;s2=0;s3=0;flag=0;len=0;
    		for(int j=i;j<=n&&j<=i+r2-1;j++){
    			if(f[j]==0)s2++;
    			else if(f[j]==1)s3++;
    			else s1++;len++;
    			if((s1+s2+s3)>=l2&&s2&&(s1+s2+s3+1)/2>s1&&(s1+s2+s3+1)/2<=s1+s2)ans++;
    		}
    	}
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    Java中接口对象实现回调
    推荐算法之计算相似度
    mahout入门实例2-Mahout单机开发环境介绍(参考粉丝日志)
    mahout入门实例-基于 Apache Mahout 构建社会化推荐引擎-实战(参考IBM)
    windows下gvim使用及常见命令
    一道C语言的问题(转)
    android开发手记一
    数据结构之有关图的算法(图的邻接表示法)
    Week of Code:GG
    HDU 5587:Array
  • 原文地址:https://www.cnblogs.com/onglublog/p/9876921.html
Copyright © 2011-2022 走看看