【NOIP2012TG】solution

D1T1(Vigenere)

题意:给你一个原串与一个密码串,问你按照题意规则加密后的密文。

解题思路:暴力模拟。

#include <stdio.h>
int k[105],c[1005],u1[105],u2[1005];
void read(int*a,int*b){
    char c;
    while(c=getchar(),c>='a'&&c<='z'||c>='A'&&c<='Z')
        if(c>='a'&&c<='z')a[++a[0]]=c-'a';
        else a[++a[0]]=c-'A',b[a[0]]=1;;
}
int main(){
    read(k,u1);read(c,u2);int j=0;
    for(int i=1;i<=c[0];i++)
    {
        j++;if(j==k[0]+1)j=1;
        c[i]=(c[i]-k[j]+26)%26;
    }
    for(int i=1;i<=c[0];i++)
        if(u2[i]==1)putchar(c[i]+'A');
           else putchar(c[i]+'a');
}

D1T2(game)

题意:n个物品,每个物品有2个val,然后按照题目当中的价值计算方式计算,求使得价值最高的物品最小的价值。

解题思路:考虑最后一个大臣,显然他很可能是金币最多的人。我们要让他的金币尽量的少。之前的大臣左手的数肯定会乘起来,所以我们要使S/A/B尽量的大。(设S 是左手所有数的乘积),即让A*B尽量的大。选完最后一个后,我们把他踢掉,然后再找一个最后的大臣。如此往复,相当于就是对A*B排序。然后就直接按照上述思路进行处理,剩下的就是高精度,压4位就可以直接计算了。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MN 1005
struct sv{
    int a,b;
    inline bool operator <(const sv &y)const{
        return a*b<y.a*y.b;
    }
}t[MN];
struct hpc{
    int num[MN],len;
    inline bool operator <(const hpc &b)const{
        if (len^b.len) return len<b.len;
        for (int i=len-1; i>=0; --i)
            if (num[i]^b.num[i]) return num[i]<b.num[i];
        return 0;
    }
    inline hpc operator *(int b)const{
        hpc ans;int &leng=ans.len;
        memset(ans.num,0,sizeof(ans.num));
        for (int i=0; i<len; ++i){
            ans.num[i]+=num[i]*b;ans.num[i+1]+=ans.num[i]/10000;
            ans.num[i]%=10000;
        }ans.len=len;
        for (; ans.num[leng]; ++leng){
            ans.num[leng+1]=ans.num[leng]/10000;ans.num[leng]%=10000;
        }return ans;
    }
    inline hpc operator /(int b)const{
        hpc ans;int &leng=ans.len;
        memset(ans.num,0,sizeof(ans.num));
        for (int i=len-1; i; --i){
            ans.num[i-1]+=(ans.num[i]+num[i])%b*10000;
            ans.num[i]=(ans.num[i]+num[i])/b;
        }
        ans.num[0]=(ans.num[0]+num[0])/b;
        for (ans.len=len; !ans.num[leng-1]; --leng);
        return ans;
    }
    inline void print(){
        printf("%d",num[len-1]);
        for (int i=len-2; i>=0; --i) printf("%.4d",num[i]);
    }
}sum,res;int n;
int main(){
    scanf("%d",&n);
    for (int i=0;i<=n;i++){
        scanf("%d%d",&t[i].a,&t[i].b);
    }sum.num[0]=t[0].a;sum.len=1;
    std::sort(t+1,t+n+1);
    for (int i=1;i<=n;i++){
        hpc tmp=sum/t[i].b;
        if (res<tmp) res=tmp;
        sum=sum*t[i].a;
    }res.print();
}

D1T3(drive)

题意:有n个点,每个点有高度,两个点的距离定义为高度差的绝对值(高度越小的默认越距离相对较小)。有2个人,1个人走最近的路,另一个人走第二近的路,现在这2人轮流开车,问:(I)给定时间t,问选哪个起点两人走的路程比值最小。(II)给定起点a与时间t,问走完后2人各自走了多少。

解题思路:首先考虑如何处理距离的问题,我们需要做到的是给定一个高度,在其左右2边进行查找,显然c++STL中的set是可以较为方便的实现的。

接下来考虑如何解决问题:首先我们将轮流走一次压缩为1步,然后倍增求出走x步的距离,然后对于每次询问就只需要倍增查找即可。(I)就是暴力扫一遍找答案,(II)就是裸的询问。时间效率( O((n+m) log n) )

#include <stdio.h>
#include <algorithm>
#include <set>
#define MN 100005
#define lg 16
#define ll long long
#define inf 1e16
#define abs(a) (((a))<0?(-1*(a)):(a))
struct point{
    int no,h;
    bool operator <(const point &b)const{
        return h<b.h;
    }
}a[MN];
struct cmpp{
    int no,dis;
    bool operator <(const cmpp &b)const{
        return dis<b.dis||(dis==b.dis&&a[no].h<a[b.no].h);
    }
}tmp[5];
using std::set; set<point> pt;
int n,m,x0,na[MN],nb[MN],ans;
ll fa[MN][lg+1],da[MN][lg+1],db[MN][lg+1],ansa=inf,ansb=0ll;
inline int in(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void prework(int k){
    set<point>::iterator it=pt.find(a[k]);
    register int j=0;
    if (it!=pt.begin()){
        --it;tmp[++j].no=it->no,tmp[j].dis=abs(it->h-a[k].h);
        if (it!=pt.begin()){
            --it;tmp[++j].no=it->no,tmp[j].dis=abs(it->h-a[k].h);++it;
        }++it;
    }
    if ((++it)!=pt.end()){
        tmp[++j].no=it->no,tmp[j].dis=abs(it->h-a[k].h);
        if ((++it)!=pt.end()){
            tmp[++j].no=it->no,tmp[j].dis=abs(it->h-a[k].h);
        }
    }
    std::sort(tmp+1,tmp+1+j);
    nb[k]=tmp[1].no;
    if (j==1) return;
    na[k]=tmp[2].no;
}
inline void getans(int u,int t,ll &ta,ll &tb){
    for (register int i=lg; i>=0; --i)
        if (fa[u][i]&&da[u][i]+db[u][i]<=t){    
            ta+=da[u][i],tb+=db[u][i];
            t-=da[u][i]+db[u][i];u=fa[u][i];
        }
    if (da[u][0]<=t) ta+=da[u][0];
}
void init(){
    n=in();for (int i=1; i<=n; ++i) a[i].h=in(),a[i].no=i;
    for (register int i=n; i; --i) {pt.insert(a[i]);if (i!=n) prework(i);}
}
void st(){
    for (register int i=1; i<=n; ++i){
        register int p1=na[i],p2=nb[na[i]];
        da[i][0]=p1?abs(a[p1].h-a[i].h):0;
        db[i][0]=p2?abs(a[p2].h-a[p1].h):0;
        fa[i][0]=p2;
    }
    for (int j=1; j<=lg; ++j)
        for (register int i=1; i<=n; ++i){
            fa[i][j]=fa[fa[i][j-1]][j-1];
            da[i][j]=da[i][j-1]+da[fa[i][j-1]][j-1];
            db[i][j]=db[i][j-1]+db[fa[i][j-1]][j-1];
        }
}
void solve(){
    st();x0=in();
    for (register int i=1; i<=n; ++i){
        register ll ta=0ll,tb=0ll;
        getans(i,x0,ta,tb);
        if (tb&&(!ans||ansa*tb>ansb*ta))
            ans=i,ansa=ta,ansb=tb;
    }
    printf("%d
",ans);m=in();
    for (register int i=1; i<=m; ++i){
        register int x=in(),t=in();
        register ll ta=0ll,tb=0ll;
        getans(x,t,ta,tb);
        printf("%lld %lld
",ta,tb);
    }
}
int main(){init();solve();return 0;}

D2T1(mod)

题意:看题目。

解题思路:裸的exgcd,注意一下答案要取最小正整数。

#include <stdio.h>
inline void exgcd(int a,int b,int &x,int &y){
    if (!b){x=1,y=0; return;}
    exgcd(b,a%b,x,y);
    int t=x;x=y;y=t-a/b*y;
}
int main(){
    int a,b,x,y;
    scanf("%d%d",&a,&b);
    exgcd(a,b,x,y);
    x=(x+b)%b;printf("%d",x);
}

D2T2(classroom)

题意:有n天,每天有r[i]间教室,有m个任务,这个任务占用一个时间段中的若干间教室,问按顺序满足最多可以满足多少间。

解题思路:二分答案,然后差分统计第i天借了教室的数量,check即可。时间效率:( O(nlog n) )

#include <stdio.h>
#include <string.h>
#define MN 1000005
#define mid (l+r>>1)
int cf[MN],n,m,s[MN],t[MN],d[MN],r[MN];
inline int in(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline bool check(int ans){
    memset(cf,0,sizeof(cf));
    for (register int i=1; i<=ans; ++i)
        cf[s[i]-1]+=d[i],cf[t[i]]-=d[i];
    register int tmp=cf[0];
    for (register int i=1; i<=n; tmp+=cf[i++])
        if (tmp>r[i]) return 0;
    return 1;
}
void init(){
    n=in(),m=in();
    for (int i=1; i<=n; ++i) r[i]=in();
    for (register int i=1; i<=m; ++i) d[i]=in(),s[i]=in(),t[i]=in();
}
void solve(){
    int l=0,r=m;
    for (;l<r; check(mid)?l=mid+1:r=mid);
    if (r==m) puts("0");
    else printf("-1
%d",r);
}
int main(){init();solve();return 0;}

D2T3(blockade)

题意:在一棵树上,有m个可以移动的东西,树的边有权值,若根到某个节点上有东西,那么就无法遍历到某个节点,问无法遍历到所有叶节点的最小时间。

解题思路:首先倍增预处理,然后考虑二分答案,紧接着对于二分出来的答案,让所有的东西在时限内尽可能的往上爬,能爬到根节点的记录下来,接下来先dfs找出根节点的子节点子树中是否存在没有所有叶节点都无法遍历到,然后贪心即可。时间效率 ( O(nlog n + (mlog {mn} + n)log Ans) ).

#include <stdio.h>
#include <algorithm>
#include <string.h>
#define MN 50005
#define lg 15
#define ll long long
#define v edge[i].to
#define mid (l+r>>1)
char B[1<<26],*S=B,C;int X;
inline int in(){
    while((C=*S++)<'0'||C>'9');
    for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0';
    return X;
}
struct edg{
    int to,nxt,val;
    bool friend operator <(const edg &a,const edg &b){
        return a.val<b.val;
    }
}edge[MN<<1],ste[MN];
struct tas{
    int tim,lstpos;
    bool friend operator <(const tas &a,const tas &b){
        return a.tim<b.tim;
    }
}task[MN];
int head[MN],n,m,l,r,cnt,fa[MN][lg+1],cntt,pos[MN],count;
bool vis[MN],isleaves[MN],used[MN];ll dis[MN];
inline void ins(int x,int y,int val){edge[++cnt].to=y,edge[cnt].nxt=head[x],head[x]=cnt,edge[cnt].val=val;}
inline void pre(int u,int f,ll d){
    fa[u][0]=f,dis[u]=d;register bool b=1;
    for (register int i=head[u]; i; i=edge[i].nxt)
        if (v!=f) b=0,pre(v,u,d+edge[i].val);
    if (b) isleaves[u]=1;
}
void init(){
    fread(B,1,1<<26,stdin);n=in();
    for (int i=1; i<n; ++i){
        register int x=in(),y=in(),val=in();
        ins(x,y,val);ins(y,x,val);
        if (x==1||y==1)
            ste[++cntt].to=x==1?y:x,ste[cntt].val=val;
    }    
    std::sort(ste+1,ste+cntt+1);
    m=in();if (m<cntt){puts("-1");return;}
    for (register int i=1; i<=m; ++i) pos[i]=in();
    pre(1,0,0);
    for (int j=1; j<=lg; ++j)
        for (register int i=1; i<=n; ++i)
            fa[i][j]=fa[fa[i][j-1]][j-1];
}
inline bool dfs(int u){
    register bool p=1;if (vis[u]) return 1;
    if (isleaves[u]) return 0;
    for (register int i=head[u]; i; i=edge[i].nxt)
        if (v!=fa[u][0]) p&=dfs(v);
    return vis[u]=p;
}
inline bool uptree(int no,int tim){
    register int u=pos[no];
    for (register int i=lg; i>=0; --i)
        if (fa[u][i]>1&&dis[pos[no]]-dis[fa[u][i]]<=tim) u=fa[u][i];
    if (fa[u][0]==1&&dis[pos[no]]<tim) {
        task[++count].lstpos=u,task[count].tim=tim-dis[pos[no]];
    }else vis[u]=1;
}
inline bool check(int ans){
    count=0;memset(vis,0,sizeof(vis));
    for (register int i=1; i<=m; ++i) uptree(i,ans);
    dfs(1);std::sort(task+1,task+count+1);
    register int p=1;
    for (register int i=1; i<=count; ++i)
        if (!vis[task[i].lstpos]) vis[task[i].lstpos]=1;
        else{
            while(vis[ste[p].to]&&p<=cntt) ++p;if (p>cntt) return 1;
            if (task[i].tim>=ste[p].val) vis[ste[p].to]=1;
        }
    while(vis[ste[p].to]) ++p;return p>cntt;
}
void solve(){
    int l=0,r=1e9;
    for (;l<r; check(mid)?r=mid:l=mid+1);
    printf("%d",r);
}
int main(){init();solve();return 0;}
原文地址:https://www.cnblogs.com/Melacau/p/NOIP2012TGsolution.html