HGOI7.5集训题解

题解

今天做了一份2012day1的真题,我真的没有想到day1会这么难ojz。玄学倍增教你做人。

第一题——Vigenère 密码(vigenere)

  • 就是给你一个图,然后给你密码和key,根据图求原码。十分简单的字符串题。
  • 其实很简单的。由图上可知,x+y=z+1(xyz分别表示原码、key、和密码)
  • 那么x=z+1-y。
  • 由于有两遍重复那么就要判断有没有要多加一个26的。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
    freopen("vigenere.in","r",stdin);
    freopen("vigenere.out","w",stdout);
}
const int MAXN=1010;
char m[MAXN],k[MAXN],c[MAXN],t[MAXN];
int main(){
    fff();
    cin>>k>>c;
    int lc=strlen(c),lk=strlen(k);
    for (int i=0;i<lc;i++){
        t[i]=c[i];
        if(c[i]>='a'&&c[i]<='z') c[i]+=('A'-'a');
    }
    for (int i=0;i<lk;i++){
        if(k[i]>='a'&&k[i]<='z') k[i]+=('A'-'a');
    }
    int temp=0;
    for (int i=0;i<lc;i++){
        m[i]=c[i]-k[temp++]+'A';
        if(m[i]<'A') m[i]+=26;
        if(t[i]>='a'&&t[i]<='z') m[i]-=('A'-'a');
        if(temp>=lk) temp=0;
    }
    cout<<m;
    return 0;
}

第二题——国王游戏(game)

  • 这是一道神奇的排序题(第一眼以为是dp)然后一直在想方程。
  • 题目大意:告诉你国王和若干大臣的左右手上的数值,除了国王在第一个外求排列使得 M a x ( j = 0 i 1 L j / R i 最小。

  • 然后我就使劲想方程ojz,日常跑偏。

  • 可以证明按照 L 1 R 1 < L 2 R 2 排列为最优解。
  • 然后就打把高精解决问题。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void fff(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout);
}
const int MAXN=1010;
const int LIMIT=10000;
int n;
struct node{
    int l,r,num;
    bool operator <(const node x) const{
        if(num==x.num){
            if(l==x.l)return r<x.r;
            return l<x.l;
        }
        return num<x.num;
    }
}a[MAXN];
int sum[MAXN<<2],siz_sum,ans[MAXN<<2],siz_ans,maxx[MAXN<<2],siz_maxx;
void multy(int x){
    int c=0;
    for (int i=1;i<=siz_sum;i++){
        sum[i]=sum[i]*x+c;
        c=sum[i]/LIMIT;
        sum[i]=sum[i]%LIMIT;
    }
    while (c>0){
        sum[++siz_sum]=c%LIMIT;
        c/=LIMIT;
    }
}
void div(int x){
    memcpy(&ans,&sum,siz_sum*sizeof(int));
    int c=0;
    siz_ans=siz_sum;
    for (int i=siz_sum;i>=1;i--){
        ans[i]=(c*LIMIT+sum[i])/x;
        c=(c*LIMIT+sum[i])%x;
    }

}
bool check(){
    if(siz_maxx>siz_ans) return false;
    else if(siz_maxx<siz_ans) return true;
    else{
        for (int i=siz_maxx;i>=1;i--){
            if(maxx[i]==ans[i]) continue;
            if(maxx[i]>ans[i]) return false;
            return true;
        }
    }
}
int main(){
    fff();
    cin>>n;
    for (int i=0;i<=n;i++){
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].num=a[i].l*a[i].r;
    }
    sort(a+1,a+n+1);
    sum[1]=a[0].l;
    siz_sum=siz_maxx=siz_ans=1;
    for (int i=2;i<=n;i++){
        multy(a[i-1].l);
        div(a[i].r);
        if(check()){
            siz_maxx=max(siz_ans,siz_maxx);
            for (int i=1;i<=siz_maxx;i++) maxx[i]=ans[i];
        }
    }
    while (maxx[siz_maxx]==0&&siz_maxx>=0) siz_maxx--;
    if(maxx[1]==0&&siz_maxx==-1) cout<<1;
    else 
    for (int i=siz_maxx;i>=1;i--){
        if(i!=siz_maxx) printf("%04d",maxx[i]);
        else printf("%d",maxx[i]);
    }

    return 0;
}

第三题——开车旅行(drive)

  • 这是题变态题(还是不知道为什么放在day1)题解说是用离散化预处理和倍增。
  • 其实有两小问。第一小问是给你x0,让你求哪个点出发的AB比值最小。
  • 第二小问是给定出发点和x,求两个人走的路。
  • 这道题果断暴力也能够拿70分。但玄学倍增能够拿满分ojz。

  • 预处理先找出从第i点出发向后的最近点和次近点。然后根据这一种方法进行st倍增做法。得到答案。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;
void fff(){
    freopen("drive.in","r",stdin);
    freopen("drive.out","w",stdout);
}
const int MAXN=100100;

struct node{
    int h,id,l,r;
    bool operator < (const node x) const{
        return h<x.h;
    }
}a[MAXN];
int n,m;
ll x;
int p[MAXN],s,nb[MAXN],na[MAXN];
int f[MAXN][21],stA[MAXN][21],stB[MAXN][21];
int l,r,j;//Pascal选手的全局变量在离散化情况下的效率会提高
ll abs1(int a){
    if(a<0) a=-a;
    return a;
}
bool zuo(){
    if(l==0) return false;
    if(r==0) return true;
    return abs1(a[l].h-a[j].h)<=abs1(a[r].h-a[j].h);
}
int pd(int x,int y){
    if(!x) return a[y].id;
    if(!y) return a[x].id;
    if(abs1(a[j].h-a[x].h)<=abs1(a[j].h-a[y].h)) return a[x].id;
    return a[y].id;
}
void st(){
    for (int j=1;j<=19;j++){
        for (int i=1;i<=n;i++){
            f[i][j]=f[f[i][j-1]][j-1];
            stA[i][j]=stA[i][j-1]+stA[f[i][j-1]][j-1];
            stB[i][j]=stB[i][j-1]+stB[f[i][j-1]][j-1];
        }
    }
}
int ta,tb,ans;
double minn=9999999999;
void getab(long long x,int p){
    ta=tb=0;
    for (int i=19;i>=0;i--){
        if(f[p][i]&&(ll)(ta+tb+stA[p][i]+stB[p][i])<=x){
            ta+=stA[p][i];
            tb+=stB[p][i];
            p=f[p][i];
        }
    }
    if(na[p]&&ta+tb+stA[p][0]<=x) ta+=stA[p][0];
}
int main(){
    fff();
    cin>>n;
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i].h);
        a[i].id=i;
    }
    sort(a+1,a+n+1);
    for (int i=1;i<=n;i++){
        p[a[i].id]=i;//指向现有的位置 
        a[i].l=i-1;
        a[i].r=i+1;
    }
    a[1].l=a[n].r=0;
    for (int i=1;i<=n;i++){
        j=p[i],l=a[j].l,r=a[j].r;
        if(zuo()){
            nb[i]=a[l].id;
            na[i]=pd(a[l].l,r);
        }else{
            nb[i]=a[r].id;
            na[i]=pd(l,a[r].r);
        }
        if(l) a[l].r=r;
        if(r) a[r].l=l;
    }
    for (int i=1;i<=n;i++){
        f[i][0]=nb[na[i]];
        stA[i][0]=abs1(a[p[i]].h-a[p[na[i]]].h);
        stB[i][0]=abs1(a[p[f[i][0]]].h-a[p[na[i]]].h);
    }
    st();
    cin>>x>>m;
    for (int i=1;i<=n;i++){
        getab(x,i);
        if(tb&&1.0*ta/tb<minn){
            ans=i;
            minn=1.0*ta/tb;
        }
    }
    printf("%d\n",ans);
    for (int i=1;i<=m;i++){
        scanf("%d%lld",&s,&x);
        getab(x,s);
        printf("%d %d\n",ta,tb);
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/80928685