NOIP2012提高组day1题解

1.Vigenère密码
  一开始以为是明码反暗码,就直接把密码表预处理,然后测了多好多遍还是没过样例,十分困惑与不解然后就看一了一遍题目(咳,
  暗码减去密码取模
  这一遍很自信然后爆零了
  (输出区分大小写
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
char m[maxn],k[maxn];
int lm,lk,key[maxn];
int main()
{
freopen("vigenere.in","r",stdin);
freopen("vigenere.out","w",stdout);
scanf("%s%s",k,m);
lm=strlen(m),lk=strlen(k);
for(int i=0;i<lk;i++)
if(k[i]>='a'&&k[i]<='z') key[i]=k[i]-'a';
else key[i]=k[i]-'A';
int j=0;
for(int i=0;i<lm;i++)
{
char tmp;
if(m[i]>='a'&&m[i]<='z') tmp='a',m[i]-='a';
else tmp='A',m[i]-='A';
m[i]-=key[j];
if(m[i]<0) m[i]+=26;
m[i]+=tmp;
j++;
if(j>=lk) j-=lk;
}
for(int i=0;i<lm;i++)
printf("%c",m[i]);
return 0;
}
2.国王游戏
手推几组数据就很容易得到一个结论:最优的方案肯定是大臣们按成绩升序排序时候的序列,然后要打高精度(想吐。初测六十,位数最高点没处理好
结论摸鱼证明:
  A1…Ax-1AxAx+1…Ay-1AyAy+1
设A1到Ax-1乘积为k1,那么Ax的钱就是k1/Bx,
设Ax+1到Ay-1乘积为k2,那么Ay的钱就是k1*Ax*k2/By
那么最优方案就是max(k1/Bx,k1*Ax*k2/By)
讲Ay和Ax互换,同理可得此时最优方案是max(k1/By,k1*k2*Ay/Bx)
易得
k1*Ax*k2/By>k1/By
k1*k2*Ay/Bx>k1/Bx

比较k1*Ax*k2/By k1*k2*Ay/Ax
很容易可以判断 大的放后面最优值就最小
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
struct ppl{
    ll a,b;
}    p[1005];


ll n,x;    
ll sum[5000],ans[5000],divn[5000];    


bool cmp(ppl x,ppl y){       
    return x.a*x.b<y.a*y.b;
}    


void compare(){
    for(ll k=max(ans[0],divn[0]);k>0;k--){    
        if(ans[k]>divn[k])return;
        if(ans[k]<divn[k]){
            for(ll j=0;j<=divn[0];j++)   
                ans[j]=divn[j];
            return;
        }
    }
    return;
}


int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
    scanf("%lld",&n);
    for(ll i=0;i<=n;i++)scanf("%lld%lld",&p[i].a,&p[i].b);


    sort(p+1,p+n+1,cmp);


    memset(ans,0,sizeof(ans));
    memset(sum,0,sizeof(sum));


    sum[0]=1;    sum[1]=1;


    for(ll i=1;i<=n;i++){


        x=0;
        for(ll j=1;j<=sum[0];j++){       
            sum[j]=sum[j]*p[i-1].a+x;
            x=sum[j]/10000;
            sum[j]%=10000;
        }


        if(x>0){        
            sum[0]++;
            sum[sum[0]]=x;
        }


        x=0;
        memset(divn,0,sizeof(divn));


        for(ll j=sum[0];j>0;j--){       
            divn[j]=(x*10000+sum[j])/p[i].b;
            x=(x*10000+sum[j])%p[i].b;
        }
        divn[0]=sum[0];
        while(divn[divn[0]]==0)divn[0]--;  


        compare(); 
    }


    printf("%lld",ans[ans[0]]);
    for(ll i=ans[0]-1;i>0;i--){     
        printf("%lld",ans[i]/1000);
        printf("%lld",ans[i]/100%10);
        printf("%lld",ans[i]/10%10);
        printf("%lld",ans[i]%10);
    }


    return 0;
}
3.开车旅行
因为第二题的高精度所以得分 0
可以直接模拟距离每个点最近和次近的点 打得好70
也可以用双向链表模拟 这个很快 但是很容易出错 100
线段树:
  线段树维护三个值,区间内最小值、最大值、数的个数。从右往左找(nn~11),当找到第ii个点时,第i+1i+1个点到第nn个点的高度值已经更新过了线段树。通过线段树找出高度比HiHi大的最小、次小值,以及找出高度比HiHi小的最大、次大值,便可以更新第ii个点的最近、次近的点的编号了。因为HiHi的值可能很大,会空间超限,所以做之前需要离散化一次
  答案会很大,需要开int64(long long)
  然后第二个小问就用倍增
  设f[i][j][0]表示从城市i出发,小A经过2^j轮(注意是轮)之后走的路程。 
  f[i][j][1]表示小B走的路程 
  g[i][j]表示走到那个城市。 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define db double
#define ll long long
#define inf 0x7fffffff
using namespace std;
struct node{
    int d;ll v;
}p[N];
struct note{
    int mx,mi;
}t[N*5];
bool cmp(node x,node y) {
    return x.v<y.v||x.v==y.v&&x.d<y.d;
}
db sum,ans;
int n,m,tot,x,y,k;
int h[N],a[N],b[N],w[N],g[N][18];
ll f[N][18][2],ana,anb,v[N];
void change(int v,int l,int r,int x) {
    if (l==r) {t[v].mx=t[v].mi=l;return;}
    int m=(l+r)/2;
    if (x<=m) change(v*2,l,m,x);
    else change(v*2+1,m+1,r,x);
    t[v].mx=max(t[v*2].mx,t[v*2+1].mx);
    t[v].mi=min(t[v*2].mi,t[v*2+1].mi);
}
int getmx(int v,int l,int r,int x,int y) {
    if (x>y) return 0;
    if (l==x&&r==y) return t[v].mx;
    int m=(l+r)/2;
    if (y<=m) return getmx(v*2,l,m,x,y);
    else if (x>m) return getmx(v*2+1,m+1,r,x,y);
    else return max(getmx(v*2,l,m,x,m),getmx(v*2+1,m+1,r,m+1,y));
}int getmi(int v,int l,int r,int x,int y) {
    if (x>y) return n+1;
    if (l==x&&r==y) return t[v].mi;
    int m=(l+r)/2;
    if (y<=m) return getmi(v*2,l,m,x,y);
    else if (x>m) return getmi(v*2+1,m+1,r,x,y);
    else return min(getmi(v*2,l,m,x,m),getmi(v*2+1,m+1,r,m+1,y));
}
void solve(int x,int y) {
    ana=anb=0;
    fd(j,17,0) 
        if (f[x][j][0]+f[x][j][1]<=y) {
            y-=f[x][j][0]+f[x][j][1];
            ana+=f[x][j][0];anb+=f[x][j][1];
            x=g[x][j];
        } 
    if (f[x][0][0]<=y) ana+=f[x][0][0];
}
int main() {
freopen("drive.in","r",stdin);
freopen("drive.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n) 
  scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i;
    sort(p+1,p+n+1,cmp);v[0]=inf;
    fo(i,1,n) h[p[i].d]=++tot,w[tot]=p[i].d;
    fo(i,1,n*5) t[i].mi=n+1;
    fd(i,n,1) {
        p[1].d=getmi(1,1,n,h[i]+1,n);p[2].d=getmx(1,1,n,1,h[i]-1);
        p[3].d=getmi(1,1,n,p[1].d+1,n);p[4].d=getmx(1,1,n,1,p[2].d-1);
        fo(j,1,4) p[j].v=abs(v[i]-v[w[p[j].d]]);    
        sort(p+1,p+5,cmp);
        if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d];
        if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d];
        change(1,1,n,h[i]);         
    }
    fo(i,1,n) {
        g[i][0]=b[a[i]];
        f[i][0][0]=abs(v[i]-v[a[i]]);
        f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]);
    }
    fo(j,1,17)
        fo(i,1,n) {
            g[i][j]=g[g[i][j-1]][j-1];
            f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
            f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
        }
    scanf("%d",&x);ans=inf;
    fo(i,1,n) {
        solve(i,x);
        if (!anb) sum=inf;else sum=ana*1.0/anb;
        if (sum<ans||sum==ans&&v[i]>v[k]) ans=sum,k=i;
    }
    printf("%d\n",k);
    for(scanf("%d",&m);m;m--) {
        scanf("%d%d",&x,&y);
        solve(x,y);
        printf("%lld %lld\n",ana,anb);  
    }
}








基础还是不踏实

猜你喜欢

转载自blog.csdn.net/beautiful_cxw/article/details/80941793