NOIP2012提高组

D1T1.Vigenère密码

模拟

#include<iostream> 
#include<cstdio> 
using namespace std; 
int main() 
{ 
    string a,b;cin>>b>>a; 
    for(int i=0;i<a.size();i++) 
    { 
        bool ok=0;if(a[i]>='a'&&a[i]<='z')a[i]=a[i]-'a'+'A',ok=1;int j=i%b.size(); 
        if(b[j]>='a'&&b[j]<='z')b[j]=b[j]-'a'+'A'; 
        int c=((a[i]-'A')-(b[j]-'A')+26)%26;if(ok==0)cout<<(char)(c+'A');else cout<<(char)(c+'a'); 
    } 
    return 0; 
} 
View Code

D1T2.国王游戏

贪心+高精度

我们知道,一个排序是经过好多次交换变成的,我们分析一下何时两个大臣需要交换,

设第一个大臣左右手数字为(a1,b1),第二个大臣左手数字为(a2,b2),第一个大臣前面的所有大臣和国王左手数字的乘积为x

那么交换前第一个大臣的奖赏为x/b1,第二个为(x*a1)/b2

交换后第一个大臣为x/b2,第二个为(x*a2)/b1

为了好看,我们设c1=x/b1,c2=(x*a1)/b2,c3=x/b2,c4=(x*a2)/b1

因为a>=1,b>=1,所以c4>=c1,c2>=c3

比较c2与c4的大小,可以发现,当a1/b2<a2/b1时,即a1*b1<a2*b2时,不交换是更优的,所以我们应该让a*b小的排在前面

记得写高精度!!!!!!!!!!!!!!!!!

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
struct HP{  
    int s,n[10001];  
}aa[1001];  
struct xxx{  
    int a,b;  
}x[1001];  
bool cmp(xxx a,xxx b)  
{  
    return a.a*a.b<b.a*b.b;  
}  
void Max(HP a,HP b,HP &c)  
{  
    memset(c.n,0,sizeof(c.n));  
    if(a.s>b.s) c=a;  
    if(a.s<b.s) c=b;  
    if(a.s==b.s)  
    {  
        for(int i=a.s;i>=1;i--)  
        {  
            if(a.n[i]>b.n[i]){c=a;break;}  
            if(a.n[i]<b.n[i]){c=b;break;}  
        }  
        if(c.s==0)c=a;  
    }  
}  
void cheng(HP a,HP b,HP &c)  
{  
    memset(c.n,0,sizeof(c.n));  
    for(int i=1;i<=a.s;i++)  
    for(int j=1;j<=b.s;j++)  
    {  
        c.n[i+j-1]+=(a.n[i]*b.n[j])%10000;  
        c.n[i+j]+=(a.n[i]*b.n[j])/10000;  
    }  
    if(c.n[a.s+b.s])c.s=a.s+b.s;  
    else c.s=a.s+b.s-1;  
}  
void chu(HP a,int b,HP &c)  
{  
    memset(c.n,0,sizeof(c.n));  
    int now=0;  
    for(int i=a.s;i>=1;i--)  
    {  
        now+=a.n[i];c.n[i]=now/b;now=now%b*10000;  
    }  
    if(c.n[a.s])c.s=a.s;else c.s=a.s-1;  
}  
int main()  
{  
    int n;scanf("%d",&n);for(int i=0;i<=n;i++)scanf("%d%d",&x[i].a,&x[i].b);  
    sort(x+1,x+n+1,cmp);  
    for(int i=0;i<=n;i++)aa[i].n[1]=x[i].a,aa[i].s=1;  
    HP MAX,tot=aa[0];chu(tot,x[1].b,MAX);cheng(tot,aa[1],tot);  
    for(int i=2;i<=n;i++)  
    {  
        HP v;chu(tot,x[i].b,v);Max(v,MAX,MAX);cheng(tot,aa[i],tot);  
    }  
    int d[5],dd=0;  
    while(MAX.n[MAX.s])  
    {  
        d[++dd]=MAX.n[MAX.s]%10;MAX.n[MAX.s]/=10;  
    }  
    for(;dd;dd--)printf("%d",d[dd]);  
    for(int i=MAX.s-1;i>=1;i--)printf("%04d",MAX.n[i]);  
    return 0;  
}  
View Code

 D1T3.开车旅行

双向链表+倍增

先用双向链表求出每个点的小A和小B能到达的点,具体做法如下:

先排序,然后按数据在原数组的位置遍历,寻找每个数左边,左边的左边,右边,右边的右边的最小值和次小值

找完一个数,删一个数。把每个点小A开一次,小B开一次,算作一次

fa[i][j]表示从i点开2^j次到达的点,aa[i][j]表示从i点开2^j次小A开的距离,bb[i][j]表示从i点开2^j次小B开的距离

fa[i][j]=fa[fa[i][j-1]][j-1];
aa[i][j]=aa[i][j-1]+aa[fa[i][j-1]][j-1];
bb[i][j]=bb[i][j-1]+bb[fa[i][j-1]][j-1];

为了避免比值是double,所以a:b<c:d当ad<cb时,成立(c必须不等于0)

最后,如果ansb一直是0的话,应输出海拔最高的

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100011],b[100011],n,m,x[10011],s[10011],bh[100011];
int fa[100011][20];long long aa[100011][20],bb[100011][20];
struct xxx{
    int h,lnext,rnext,no;
}data[100011];
bool cmp(xxx a,xxx b){return a.h<b.h;}
void gx(int _h,int _no,int h,int i,int &Min)
{
    if(_h<=Min)
    {
        if(_h==Min)a[i]=_no;
        else {a[i]=b[i];Min=_h;b[i]=_no;}
    }
    else if(abs(h-data[bh[a[i]]].h)>_h)a[i]=_no;
}
void ab()
{
    data[0].lnext=0;data[0].rnext=0;data[0].h=2000000003;
    for(int i=1;i<=n;i++)data[i].lnext=i-1,data[i].rnext=i+1;
    data[n].rnext=0;
    for(int i=1;i<=n;i++)
    {
        int Min=1000000001;
        int no=data[bh[i]].no,h=data[bh[i]].h,l=data[bh[i]].lnext,r=data[bh[i]].rnext;
        int l2h,l1h,r1h,r2h,l2no,l1no,r1no,r2no,l2,l1,r1,r2;
        l1=data[l].lnext;l2=data[l1].lnext;
        r1=data[r].rnext;r2=data[r1].rnext;
        l1h=data[l].h;l2h=data[l1].h;
        r1h=data[r].h;r2h=data[r1].h;
        l1no=data[l].no;l2no=data[l1].no;
        r1no=data[r].no;r2no=data[r1].no;
        if(l1)gx(h-l2h,l2no,h,i,Min);
        if(l)gx(h-l1h,l1no,h,i,Min);
        if(r)gx(r1h-h,r1no,h,i,Min);
        if(r1)gx(r2h-h,r2no,h,i,Min);
        if(l)data[l].rnext=r;if(r)data[r].lnext=l;
    }
}
void st()
{
    for(int i=1;i<=n;i++)
    {
        if(a[i])aa[i][0]=abs(data[bh[i]].h-data[bh[a[i]]].h);
        if(b[a[i]])bb[i][0]=abs(data[bh[a[i]]].h-data[bh[b[a[i]]]].h);
        fa[i][0]=b[a[i]];
     } 
    for(int j=1;j<=16;j++)
    for(int i=1;i<=n;i++)
    {
        fa[i][j]=fa[fa[i][j-1]][j-1];
        aa[i][j]=aa[i][j-1]+aa[fa[i][j-1]][j-1];
        bb[i][j]=bb[i][j-1]+bb[fa[i][j-1]][j-1];
    }
}
void Ans(int u,int x,long long &ansa,long long &ansb)
{
    for(int i=16;i>=0;i--)
    {
        if(fa[u][i]&&aa[u][i]+bb[u][i]<=x)
        {
            ansa+=aa[u][i],ansb+=bb[u][i];
            x=x-aa[u][i]-bb[u][i];u=fa[u][i];
        }
    }
    if(aa[u][0]<=x)ansa+=aa[u][0];
}
int main()
{
    scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&data[i].h);data[i].no=i;}
    sort(data+1,data+n+1,cmp);for(int i=1;i<=n;i++)bh[data[i].no]=i;
    ab();st();int x;
    scanf("%d%d",&x,&m);
    long long ans=data[n].no,ansa=0ll,ansb=0ll;Ans(data[n].no,x,ansa,ansb);
    for(int i=n-1;i>=1;i--)
    {
        int u=data[i].no;
        long long aa=0ll,bb=0ll;Ans(u,x,aa,bb);
        if(ansa==0||aa*ansb<bb*ansa)ansa=aa,ansb=bb,ans=u;
    }
    if(ansb==0)ans=data[n].no;
    printf("%lld\n",ans);
    for(int i=1;i<=m;i++)
    {
        int s,x;scanf("%d%d",&s,&x);
        long long aa=0ll,bb=0ll;Ans(s,x,aa,bb);
        printf("%lld %lld\n",aa,bb);
    }
    return 0;
}
View Code

 D2T1.同余方程

ax ≡ 1 (mod b)意味着ax-by==1,求x

exgcd,ax-by==1可以看成ax+b(-y)==1

#include<iostream> 
#include<cmath> 
using namespace std; 
int x,y; 
void gcd(int a,int b) 
{ 
    if(b==0){x=1;y=0;} 
    else
    { 
        gcd(b,a%b); 
        int t=x;x=y;y=t-a/b*y; 
    } 
} 
int main() 
{ 
    int a,b; 
    cin>>a>>b; 
    gcd(a,b); 
    x=(x%b+b)%b; 
    cout<<x; 
    return 0; 
} 
View Code

D2T2.借教室

二分+差分

由于剩余的教室是非递增的,所以我们可以二分答案

差分数组cf[]来差分r[]数组,cf[i]=r[i]-r[i-1]

每次租借s到t的d间教室,只要cf[s]-=d;cf[t+1]+=d;即可

然后还原原数组,如果剩余教室个数<0,就借不到这份订单,反之可以

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
int cf[1000002],n,m,d[1000001],s[1000001],t[1000001],r[1000001];  
bool check(int ans)  
{  
    for(int i=1;i<=n+1;i++) 
    { 
        cf[i]=r[i]-r[i-1]; 
    } 
    for(int i=1;i<=ans;i++)  
    {  
        cf[s[i]]-=d[i];cf[t[i]+1]+=d[i];  
    }  
    int last=0;  
    for(int i=n;i>=1;i--)  
    {  
        int x=last-cf[i+1];last=x;if(x<0)return false;  
    } 
    return true;  
}  
int main()  
{  
    scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&r[i]);  
    for(int i=1;i<=m;i++)scanf("%d%d%d",&d[i],&s[i],&t[i]);  
    int l=1,r=m+1,ans=0;  
    while(l<r){  
        int mid=(l+r)/2;  
        if(check(mid))l=mid+1;  
        else ans=mid,r=mid;  
    }  
    if(ans==0)cout<<0;  
    else cout<<-1<<endl<<ans;  
    return 0;  
}  
View Code

转载于:https://www.cnblogs.com/lher/p/6841786.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值