ACM ICPC 2008–2009, NEERC, Northern Subregional Contest St Petersburg(Gym 100623)

PROBLEM B C D E F H I K ARE INCLUDED

Problem B. Billboard

题意:

           有个公告板,大小为h*w,要贴n张公告,每个公告的长度是k,高度固定为1,公告放的要尽可能靠上并尽可能靠左,每给出一张公告,要求这个公告在满足要求的情况下放在了第几层。

思路:

            按照线段树的做法的话,因为公告的高度固定为1,可以对公告板的高度进行切分,将其现在的宽度值存起来,然后每次遍历从左子树开始往下走,知道走到叶子节点满足要求即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define mi (l+r)>>1
const int MAXN=200000+100;
int n,m,k,tree[MAXN*4],v,pos,t;
void push_up(int rt){
    tree[rt]=max(tree[rt*2+1],tree[rt*2]);
    return ;
}
void query(int l,int r,int rt){
    if(tree[rt]<v||pos!=-1) return ;
    if(l==r){
        tree[rt]-=v;
        pos=l;
        return ;
    }
    int mid=mi;
    query(ls);
    query(rs);
    push_up(rt);
    return ;
}
int main(){
    freopen("billboard.in","r",stdin);
    freopen("billboard.out","w",stdout);
    int n,m;
    while(scanf("%d%d%d",&n,&m,&t)!=-1){
        n=min(t,n);
        for(int i=1;i<n*4;i++) tree[i]=m;
        for(int i=0;i<t;i++){
            pos=-1;
            scanf("%d",&v);
            if(v<=m) query(1,n,1);
            printf("%d\n",pos);
        }
    }
}
Problem C. Class

题意:

         k 个人,安排坐在一个 N * M 的教室里,问每一行和每一列中最小的人数最大是多少。

思路:

         选一排一列横铺纵铺即可。

代码:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    freopen("class.in","r",stdin);
    freopen("class.out","w",stdout);
    int k,n,m,a[104][104];
    while(cin>>k>>n>>m){
        int minn=(k+1)/2;
        memset(a,0,sizeof(a));
        for(int i=1;i<=minn&&i<=n;i++)
            a[i][1]='#',k--;
        for(int i=2;i<=minn&&i<=m;i++)
            a[1][i]='#',k--;
        cout<<min(min(n,m),minn)<<endl;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(a[i][j]=='#')
                    cout<<'#';
                else{
                    if(k){
                        cout<<'#';
                        k--;
                    }else{
                        cout<<'.';
                    }
                }
            }
            cout<<endl;
        }

    }
}

Problem D. Deposits

题意:

         给出长度为 N 的 a 数列,和长度为 M 的 b 数列,b 数列中的一个数和 a 数列中的一个 b[i] 的倍数可以组成一对,问能组成多少对?

思路:

         分别记录每个数在 a 中出现的次数和在 b 中出现的次数。遍历并统计每个在 b 中的数的倍数。考虑到数据的最大值,算法的计算次数最高大致在 2千万 次以下。

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e6+1000;
long long a[MAXN],n,m,b[MAXN],x,ans;
int main()
{
    freopen("deposits.in","r",stdin);
    freopen("deposits.out","w",stdout);
    while(scanf("%d",&n)!=-1){
        ans=0;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=0;i<n;i++)
            scanf("%d",&x),
            a[x]++;
        scanf("%d",&m);
        for(int i=0;i<m;i++){
            scanf("%d",&x);
            b[x]++;
        }
        for(int i=0;i<MAXN;i++){
            if(b[i])
                for(int j=1;j*i<MAXN;j++)
                    ans+=b[i]*a[i*j];
        }
        cout<<ans<<endl;
    }
}
Problem E. Enchanted Mirror

题意:

         给出 str1 ,str2 ,str3 ,str4.问是否可以通过操作改变 str1 和 str2 ,使得 str1 == str3 ,str2 == str4.

         操作为:只允许交换 str1 的任意两个位置上的字符。

                       交换 str1 的 i 位置 和 j位置 时,str2 的 len - i + 1 位置 和 len - j + 1 位置也会交换。(例 len==4,str1==str2=="1234",交换 str1 的 '3' 和 '4' 时 str2 的 '1' 和 '2' 也同时交换)。

思路:

          尝试交换后发现先交换和后交换某一位置并不影响能否满足题意,故先将 str1 匹配至 str3 ,再利用 str1 中相同字母间的交换调节好 str2 .

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int main()
{
    freopen("enchanted.in","r",stdin);
    freopen("enchanted.out","w",stdout);
    char s[4][110];
    while(scanf("%s",s[0])==1){
        for(int i=1;i<4;++i)
            scanf("%s",s[i]);
        int l=strlen(s[0]);
        for(int i=0;i<l;++i){
            if(s[0][i]!=s[2][i]){
                for(int j=i+1;j<l;++j){
                    if(s[2][i]==s[0][j]){
                        swap(s[0][i],s[0][j]);
                        swap(s[1][l-i-1],s[1][l-j-1]);
                        break;
                    }
                }
            }
        }
        if(strcmp(s[0],s[2])==0)
        {
            for(int i=0;i<l;++i){
                if(s[1][i]!=s[3][i]){
                    bool flag=0;
                    for(int j=i+1;j<l;++j){
                        if(s[3][i]==s[1][j]&&s[0][l-i-1]==s[0][l-j-1]){
                            swap(s[1][i],s[1][j]);
                            flag=1;
                            break;
                        }
                    }
                    if(!flag) break;
                }
            }
            if(strcmp(s[1],s[3])==0)
                puts("Yes");
            else
                puts("No");
        }
        else
            puts("No");
    }
    return 0;
}

题意:

思路:

          一个找规律的题,b[2]=a[2]要求a[1]=0,b[4]=a[4]要求a[2]+a[3]=0,b[6]=a[6]要求a[5]=0,b[8]=a[8]要求a[4]+a[6]+a[7]=0,到这里就可以看出来规律了,一个偶数可以对2^n取余为0,则要求他前面的n个数和为0,这个n个数的递减规律是1.1.2.4.8.16....2^n

代码:

#include <bits/stdc++.h>
using namespace std;  
int main()  
{  
    freopen("fenwick.in","r",stdin);  
    freopen("fenwick.out","w",stdout);  
    int t,n,flag;  
    long long a[100005],sum;  
    long long n2[16]={2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};  
    long long b[16]={1,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384};  
    while(cin>>n){  
        for(int i=1;i<=n;i++)  
            scanf("%lld",&a[i]);  
        for(int i=2;i<=n;i+=2){  
            sum=0;  
            flag=-1;  
            t=i;  
            for(int j=0;j<16;j++){  
                if(i%n2[j]==0)  
                    flag=j;  
                else  
                    break;  
            }  
            for(int j=0;j<flag;j++){  
                t-=b[j];  
                sum+=a[t];  
            }  
            t-=b[flag];  
            a[t]=-sum;  
        }  
        for(int i=1;i<=n;i++)  
            i==n?cout<<a[i]<<endl:cout<<a[i]<<" ";  
    }  
    return 0;  
} 

Problem H. Holes

题意:

         输出一个最小的数,数字中有 N 个孔。不能有前导零。

思路:

         N==0 时输出一个 0

         否则奇数先输出一个 4 ,其他输出 8。

代码:

#include <bits/stdc++.h>
using namespace std;

int main(){
    freopen("holes.in","r",stdin);
    freopen("holes.out","w",stdout);
    int n;
    while(cin>>n){
        if(n==0){
            cout<<1<<endl;
        }else if(n==1){
            cout<<0<<endl;
        }else{
            if(n%2) cout<<4;
            for(int i=1;i<=n/2;i++) cout<<8;
            cout<<endl;
        }
    }
}

Problem I. Important Wires

题意:

         六级阅读材料。给出 A B C........,几个表达式,输出一个包含 A B C.......且值为true的表达式。

思路:

         请输出 A || B || C...........|| !A

代码:

#include <bits/stdc++.h>
using namespace std;
int n;
string str;
int main(){
    freopen("important.in","r",stdin);
    freopen("important.out","w",stdout);
    while(cin>>n){
        getchar();
        cout<<"Yes"<<endl;
        for(int i=0;i<n;i++){
            getline(cin,str);
            //cout<<str<<endl;
            cout<<str[0]<<"|";
        }
        cout<<"~"<<str[0];
    }
}

Problem K. Key to Success

题意:

          给你n枚硬币,让你往里面添加m枚硬币,使得这n+m枚硬币,能组成1到x的所有的数,且x尽可能的大 

思路:

          我们就从头开始计数,首先必须要有一个1,然后我们就从头开始加,如果sum小于下一个数-1,那么这中间的数就拼不出来了,我们就要放一个sum+1上去,这样一直跑下去,放完为止。

          我们可以多思考一下,类似于二进制,我们只要有了 1,2,4,8,16,32..........这些数我们就可以一直配下去,相当于每一个二进制位我们都可以配为 1 ,所以我们可以得到所有数。

代码:

#include <bits/stdc++.h>
using namespace std;
long long a[70],ans[70];
int main(){
    freopen("key.in","r",stdin);
    freopen("key.out","w",stdout);
    int n,m,x;
    while(cin>>n>>m){
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
            cin>>a[i];
        long long sum;
        for(int k=0;k<m;k++){
            sum=0;
            sort(a,a+n+k+1);
            for(int i=0;i<=n+k;i++){
                sum+=a[i];
                if(sum+1<a[i+1]){
                    a[n+k+1]=sum+1;
                    ans[k]=sum+1;
                    break;
                }
            }
            if(a[n+k+1]==0){
                a[n+k+1]=sum+1;
                ans[k]=sum+1;
            }
        }
        for(int i=0;i<m;i++)
            if(i) cout<<' '<<ans[i];
            else cout<<ans[i];
        cout<<endl;
    }

}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值