BOJ 2014新生暑假个人排位赛04 整合

44 篇文章 3 订阅

A. 大家一起点外卖

大数据

排序之后会超时, 需要使用基数排序

特别需要牢记教训


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <climits>
 
#define MAXN 500005
#define eps 1e-5
#define MOD 1000000007
 
#define test
 
#define For(i,m,n) for(int i=(m);i<(n);i++)
#define vecfor(iter,a) for(vector<int>::iterator iter=a.begin();iter!=a.end();iter++)
#define rep(i,m,n) for(int i=(m);i<=(n);i++)
#define LL long long
 
/*author birdstorm*/
using namespace std;
 
template<class T>
inline bool read(T &n){
    T x = 0, tmp = 1; char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == EOF) return false;
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return true;
}
 
template <class T>
inline void write(T n) {
    if(n < 0) {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n) {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}
int p[MAXN];
int aa[MAXN<<2];
int m, tmp;
 
int main()
{
    int t, n;
    read(t);
    while(t--){
        read(n), read(m);
        memset(aa,0,sizeof(aa));
        int g;
        For(i,0,n) read(g), aa[g]++;
        int top=0;
        bool isok=false;
        int m1, m2;
        rep(i,0,1000000){
            if(i>m-i) break;
            if(i==m-i&&aa[i]>=2){
                m1=i,m2=i,isok=true;
                break;
            }
            if(i!=m-i&&aa[i]&&aa[m-i]){
                m1=i, m2=m-i; isok=true;
            }
        }
        if(!isok) printf("Sad");
        else printf("%d %d",m1,m2);
        puts("");
    }
    return 0;
}


B. 田田的公司


十分裸的并查集, 需要注意在合并操作时, 如果两个公司已经在同一联盟下, 不能更新答案.



C. 崔逗逗的难题


纯数学. 先计算中心区域, 可以通过菱形+四个弓形得到

在计算复杂图形是要想办法将复杂图划分为简单图: 四边形, 三角形, 圆, 弓形, 扇形, 等


#include <cstdio>
#include <cstdlib>
#include <cmath>
#define For(i,m,n) for(int i=(m);i<(n);i++)
/*author birdstorm*/
using namespace std;
const double pi=acos(-1.0);
int main()
{
    double n;
    while(~scanf("%lf",&n)){
        double g=2-sqrt(3);
        double c=(pi-2.0)*n*n, b;
        double a=n*n*g+n*n*(pi/3.0-1);
        c=c-a*2.0; b=n*n-a-c;
        printf("%f %f %f\n",a,c,b);
    }
    return 0;
}



D. 崔逗逗给你信心


#打表, 数学#

十分可惜的一题, 做出来了之后提交手速慢了几秒钟, 比赛已经结束了

首先是打表观察

符合条件的数似乎没有任何联系

但是用二进制打表之后, 可以明显发现所有数转成二进制后都不会出现相邻1. 仔细观察后发现所有没有出现相邻1的二进制数都是一个解

接下来可以证明: (x*3)异或(x*2)异或(x)的值为0 的充要条件为x为一个无相邻二进制1的数

注意到乘以2的操作其实就是将二进制左移了一位, 这样3*x的答案其实就是2*x+x, 也就是二进制左移一位后相加.

也就是说: (2*x)异或(x) 必然是(3*x) 充分性即证.

接下来证明必要性:

反证设有 x 含有相邻二进制1, 且(x)异或(x*2)=x*3

则 x+2*x的值必然会在相邻1处发生进位为0的情况, 和原假设不符

必要性证毕


接下来就是计算第n个符合条件的值


可以发现所有的符合条件的数(不包括0和1)都会包含一个后缀子串, 使得这个子串符合条件.

比如 

100001001

包含    1001

对这部分进行计算, 可以发现每一个二进制1都代表了斐波那契数列的前缀和

我们可以很简单的进行预处理, 然后构造出最小的符合条件的二进制序列


构造方法如下: 从二进制序列头部开始扫描, 直到发现相邻1, 然后将后一个1变为0, 之后的串变为101010...10的序列

贪心方法的正确性显然.

所以我们可以写出代码:


#include <iostream>
#include <cstdio>
#include <cstdlib>
#define MAXN 100005
#define MOD 1000000009
#define For(i,m,n) for(int i=(m);i<(n);i++)
#define LL long long
/*author birdstorm*/
using namespace std;
LL dp[505], sum[505];
void change(int *s, int w)
{
    for(int i=w-2;i>=0;i--){
        if(s[i]==1&&s[i+1]==1){
            s[i]=0;
            for(int j=i-1;j>=0;j--){
                if(j%2==i%2) s[j]=0;
                else s[j]=1;
            }
        }
    }
}
int main()
{
    LL n;
    int c[100];
    dp[0]=0; dp[1]=1; sum[0]=1; sum[1]=1;
    For(i,2,500) dp[i]=(dp[i-1]+dp[i-2])%MOD, sum[i]=(sum[i-1]+dp[i])%MOD, sum[i-1]=(sum[i-1]+1)%MOD;
    while(~scanf("%lld",&n)){
        LL ans=1;
        int ccnt=0;
        while(n){
            c[ccnt]=(n&1);
            ccnt++; n>>=1;
        }
        change(c,ccnt);
        For(i,0,ccnt){
            if(c[i]) ans=(ans+sum[i])%MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


E. 焦级长搭积木


dp, dp公式容易很想出来, 

dp[m][h][n]=dp[m-1][h-1][n-m]+dp[m+1][h-1][n-m] 保存在m,h,n状态下的方案数

三维代表起点, 高度和总和

每次m,h,n查询前做一次记忆化搜索 复杂度为O(6*2^10)

接下来对于第k个字典序的值, 通过每次都判断/减去对应的方案数 即可O(h) 解决


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <algorithm>
#include <climits>
 
#define MAXN 100005
#define eps 1e-5
#define MOD 1000000009
 
#define test
 
#define For(i,m,n) for(int i=(m);i<(n);i++)
#define vecfor(iter,a) for(vector<int>::iterator iter=a.begin();iter!=a.end();iter++)
#define rep(i,m,n) for(int i=(m);i<=(n);i++)
#define LL long long
 
/*author birdstorm*/
using namespace std;
LL dp[11][61][580];
 
LL dfs(int first,int length, int SUM)
{
    LL &ret=dp[first][length][SUM];
    if(ret!=-1) return ret;
    if(SUM<=0) return ret=0;
    ret=0;
    if(length==1){
        if(SUM==first) return ret=1;
        else return ret=0;
    }
    if(first!=10&&SUM>first) ret+=dfs(first+1,length-1,SUM-first);
    if(first!=1&&SUM>first) ret+=dfs(first-1,length-1,SUM-first);
    return ret;
}
int sta[100], top;
bool print(int first,int length,int sum,LL k)
{
    int i;
    if(k==0&&length==1&&sum==first){
        sta[top++]=first;
        return true;
    }
    if(sum<=0||length==0) return false;
    //printf("(sum=%d)\n",sum);
    if(first!=1&&k<dp[first-1][length-1][sum-first]) {
        if(print(first-1,length-1,sum-first,k)){
            sta[top++]=first;
            return true;
        }
    }
    if(first==1){
        if(print(2,length-1,sum-1,k)){
            sta[top++]=first;
            return true;
        }
    }
    if(first!=10&&k>=dp[first-1][length-1][sum-first]){
        k-=dp[first-1][length-1][sum-first];
        if(print(first+1,length-1,sum-first,k)){
            sta[top++]=first;
            return true;
        }
        k+=dp[first-1][length-1][sum-first];
    }
    if(first==10){
        if(print(9,length-1,sum-10,k)){
            sta[top++]=first;
            return true;
        }
    }
    return false;
}
 
int main()
{
    int n, h, m;
    LL k;
    memset(dp,-1,sizeof dp);
    while(~scanf("%d%d%d",&n,&h,&m)){
        top=0;
        dfs(m,h,n); printf("%lld\n",dp[m][h][n]);
        while(scanf("%lld",&k),k!=-1){
            top=0; k--;
            int tm=m;
            if(m!=1&&k<dp[m-1][h-1][n-m]) tm=m-1;
            else if(m!=10) k-=dp[m-1][h-1][n-m], tm=m+1;
            if(m==10) tm=9;
            if(m==1) tm=2;
            printf("%d",m);
            print(tm,h-1,n-m,k);
            for(int i=top-1; i>=0; i--) printf(" %d",sta[i]);
            putchar('\n');
        }
    }
    return 0;
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值