百度之星2016初赛(第二场) -- Astar Round2B

1003

  签到题,其实就是输出一个取模后的组合数,需要用到乘法逆元。

#include <iostream>    
#include <stdio.h>    
#include <cmath>    
#include <algorithm>    
#include <string>  
#include <string.h> 
#include <set>    
#include <vector>    
#include <queue>    
#include <stack>
#include <map>

using namespace std;

#define ll long long

ll mod=1e9+7;  

ll c[1010];  

//扩展欧几里德   
void ExEuclid(ll a,ll b,ll &x,ll &y,ll &q){    
    if(b==0){    
        x=1;y=0;q=a;    
        return;    
    }    
    ExEuclid(b,a%b,y,x,q);    
    y-=x*(a/b);    
}    

//乘法逆元   
ll inv(ll num){    
    ll x,y,q;    
    ExEuclid(num,mod,x,y,q);    
    if(q==1)return (x+mod)%mod;    
}  

ll fab[1000010];  

//组合数   
ll C(ll n,ll k){  
    ll res=fab[n]*inv(fab[k]);  
    res%=mod;  
    res*=inv(fab[n-k]);  
    res%=mod;  
    return res;  
}  

int main(){
    fab[0]=1;  
    for(int i=1;i<=1000000;i++){  
        fab[i]=fab[i-1]*i;  
        fab[i]%=mod;  
    }  

    int n,m;
    while(cin>>n>>m){
        int N=n+m-4;
        int K=min(n,m)-2;
        cout<<C(N,K)<<endl;
    }
    return 0;
}

1006

  由于每个数均不相等,枚举 ai ,统计它作为中位数有多少种情况。方法是从下标 i 开始先往前扫一遍,得到每个前缀比ai大的数-比 ai 小的数有多少个,记录在一个数组里;然后再从下标 i 开始往后扫一遍,得到后缀的情况,与前缀寻求互补。

#include <iostream>    
#include <stdio.h>    
#include <cmath>    
#include <algorithm>    
#include <string>  
#include <string.h> 
#include <set>    
#include <vector>    
#include <queue>    
#include <stack>
#include <map>

using namespace std;

#define ll long long

int a[8010];
int ans[8010];

int tmp[16010];

int main(){
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        memset(ans,0,sizeof(ans)); 

        for(int i=1;i<=n;i++){
            memset(tmp,0,sizeof(tmp));

            int delta = 0;
            for(int j=i;j>=1;j--){
                if(a[j]>a[i]){
                    delta++;
                }else if(a[j]<a[i]){
                    delta--;
                }
                tmp[8000+delta]++;
            }

            delta = 0;
            for(int j=i;j<=n;j++){
                if(a[j]>a[i]){
                    delta++;
                }else if(a[j]<a[i]){
                    delta--;
                }
                ans[i] += tmp[8000-delta];
            }
        }

        for(int i=1;i<=n;i++){
            printf("%d",ans[i]); 
            if(i<n){
                printf(" ");
            }else{
                printf("\n");
            }
        }
    }
    return 0;
}

1005

  数据结构题。枚举每个ai作为最终 k 个区间交的左端点,二分搜右端点可以到达的最远位置。需要对区间左端点升序排序,把左端点合法的区间纳入考虑范围,并用树状数组维护它们的右端点。复杂度O(nlog2(n))

#include <iostream>    
#include <stdio.h>    
#include <cmath>    
#include <algorithm>    
#include <string>  
#include <string.h> 
#include <set>    
#include <vector>    
#include <queue>    
#include <stack>
#include <map>

using namespace std;

#define ll long long

int a[100010];
ll preSum[100010];

struct Seg{
    int l,r;
    bool operator<(const Seg &other)const{
        return l<other.l;
    }
}segs[100010];

int c[100010];

int lowbit(int x){
    return x&(-x);
}

int n,k,m;

void update(int pos){
    while(pos<=n){
        c[pos]++;
        pos+=lowbit(pos);
    }
}

int query(int pos){
    int res = 0;
    while(pos){
        res+=c[pos];
        pos-=lowbit(pos);
    }
    return res;
}

void init(){
    memset(c,0,sizeof(c));
}

int main(){
    while(cin>>n>>k>>m){
        init();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }

        for(int i=1;i<=n;i++){
            preSum[i] = preSum[i-1] + a[i];
        }

        for(int i=1;i<=m;i++){
            scanf("%d%d",&segs[i].l,&segs[i].r);
        }

        sort(segs+1,segs+m+1);

        //枚举
        int K=1; 
        ll ans = 0;
        for(int i=1;i<=n;i++){
            while(K<=m && segs[K].l<=i){
                update(segs[K].r);
                K++;
            }
            int l = i;
            int r = n;
            int mid;
            int res = -1;
            while(l<=r){
                mid = (l+r)>>1;
                int cnt = query(n) - query(mid-1);
                if(cnt>=k){
                    l=mid+1;
                    res = mid;
                }else{
                    r=mid-1;
                }
            }
            if(res!=-1){
                ans = max(ans,preSum[res]-preSum[i-1]);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

1001

  题目特意强调了数据随机(不然就做不了了)。。方法是枚举每个 ai 作为区间最大值,同时向左右拓展,直到左右都比 ai 大或者到达边界,更新每个长度的最大价值。此题解题依据为玄学(实际上复杂度一定不会超过 O(nlog(n)) )。
  下面强行用数学解释一下玄学。先考虑最大的数,枚举到这个数时一定会拓展到左右边界才停止,也就是扩展 n 次。对于第二大的数,平均情形下,只会扩展n/2次;第三大的数平均扩展 n/3 次。于是我们可以根据调和级数得到 nlog(n) 的复杂度。这和快速排序很像,平均 O(nlog(n)) ,最坏情形 O(n2)

#include <iostream>    
#include <stdio.h>    
#include <cmath>    
#include <algorithm>    
#include <string>  
#include <string.h> 
#include <set>    
#include <vector>    
#include <queue>    
#include <stack>
#include <map>

using namespace std;

#define ll long long


ll a[100010];

ll ans[100010];

int main(){

    int n;
    while(cin>>n){
        memset(ans,0,sizeof(ans));

        for(int i=1;i<=n;i++){
            scanf("%I64d",&a[i]);
            ans[1] = max(ans[1],a[i]*a[i]);
        }

        for(int i=1;i<=n;i++){
            int l=i;
            int r=i;
            int len = 1;
            int MIN = a[i];
            while(l>1 || r<n){

                if(a[l-1]>a[i] && a[r+1]>a[i]){
                    break;
                }
                len++;
                if(a[l-1]>a[i] && r<n || (l==1 && a[r+1]<=a[i])){
                    r++;
                    if(a[r]<MIN){
                        MIN=a[r];
                    }
                }else if(a[r+1]>a[i] && l>1 || (r==n && a[l-1]<=a[i])){
                    l--;
                    if(a[l]<MIN){
                        MIN=a[l];
                    }
                }else if(l>1 && r<n && a[l-1]<=a[i] && a[r+1]<=a[i]){
                    if(a[l-1]>a[r+1]){
                        l--;
                        if(a[l]<MIN){
                            MIN=a[l];
                        }
                    }else{
                        r++;
                        if(a[r]<MIN){
                            MIN=a[r];
                        }
                    }
                }else{
                    break;
                }
                ans[len] = max(ans[len],a[i]*MIN);
            }
        }

        for(int i=1;i<=n;i++){
            printf("%I64d\n",ans[i]);
        }

    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值