Wannafly挑戰賽6 A(二分)B(dfs)C(公式推導)D(思維)E(技巧)

A題:

題目描述

多次查詢[l,r]範圍內的完全平方數個數

定義整數x爲完全平方數當且僅當可以找到整數y使得y*y=x

輸入描述:

第一行一個數n表示查詢次數
之後n行每行兩個數l,r

輸出描述:

對於每個查詢,輸出一個數表示答案
示例1

輸入

5
1 3
1 4
2 4
4 4
1 1000000000

輸出

1
2
1
1
31622

備註:

n <= 100000
0<= l <= r <= 1000000000


題解:

給出的查詢次數很多,故不能暴力枚舉,很容易想到用二分

最後注意 0 也是平方數,並且二分查找得到的數字並不一定在區間內部


#include<math.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
 
int a[40000],len;
void init(){
    len=sqrt(1000000000.0)+1;
    for(int i=1;i<=len;i++)
        a[i]=i*i;
}
 
int binary_search(int key){
    int left=0,right=len,mid,ans;
    while(left<=right){
        mid=(left+right)/2;
        if(a[mid]<=key) ans=mid,left=mid+1;
        else            right=mid-1;
    }
    return ans;
}
 
int main()
{
    init();
    int n,l,r;
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d%d",&l,&r);
        int x=binary_search(l);
        int y=binary_search(r);
        int ans=y-x+1;
        int t=sqrt(l*1.0);
        if(t*t!=l) ans--;
        printf("%d\n",ans);
    }
    return 0;
}


B題:

題目描述

你在打比賽,這場比賽總共有12個題

對於第i個題,你的隊伍有a[i]的機率解決她

如果解決不了她呢?

由於所有人討論的都很大聲

所以你有b[i]的概率從左邊那個隊那裏聽會這個題的做法

有c[i]的概率從右邊那個隊那裏聽會這個題的做法

請問最終你們隊伍解出0-12題的概率分別是多少

輸入描述:

第一行12個數表示a[1] -> a[12]
第二行12個數表示b[1] -> b[12]
第三行12個數表示c[1] -> c[12]

輸出描述:

輸出13行,第i行表示解出i-1題的概率
保留6位小數
示例1

輸入

0.20 0.30 0.37 0.40 0.45 0.50 0.57 0.60 0.75 0.76 0.77 0.83
0.85 0.88 0.90 0.94 0.100 0.104 0.105 0.107 0.115 0.120 0.122 0.125
0.128 0.130 0.134 0.140 0.149 0.150 0.152 0.155 0.170 0.183 0.203 0.240

輸出

0.000000
0.000000
0.000000
0.000011
0.000160
0.001508
0.009620
0.041938
0.124153
0.243773
0.301960
0.212453
0.064424

題解:

看清楚題意就很容易做,題目求做出來0 個題目的概率,到做出來12個題目的概率分別是多少

先計算出每一道題目做不出的概率:自己做不出*聽不懂兩邊的人的題解

然後dfs暴力枚舉當做出來 i 道題目的概率即可


#include<stdio.h>
#define maxn 20
double a[maxn],b[maxn],c[maxn],d[maxn];;
 
double dfs(int now,int num){
    double ans=1;
    if(now==13&&num) return 0;
    if(now==13) return ans;
    if(num==0){
        for(int i=now;i<=12;i++)
            ans*=(1-d[i]);
        return ans;
    }
    return ans*(dfs(now+1,num-1)*d[now]+dfs(now+1,num)*(1-d[now]));
}
 
int main()
{
    //freopen("in.txt","r",stdin);
    for(int i=1;i<=12;i++)
        scanf("%lf",&a[i]);
    for(int i=1;i<=12;i++)
        scanf("%lf",&b[i]);
    for(int i=1;i<=12;i++)
        scanf("%lf",&c[i]);
 
    double ans=1;
    for(int i=1;i<=12;i++)
        d[i]=1-(1-a[i])*(1-b[i])*(1-c[i]),ans*=(1-d[i]);
    printf("%0.6lf\n",ans);
    for(int i=1;i<=12;i++){
        ans=dfs(1,i);
        printf("%0.6lf\n",ans);
    }
    return 0;
}


C題:

題目描述

求所有長度爲n的01串中滿足如下條件的二元組個數:
設第i位和第j位分別位ai和aj(i<j),則ai=1,aj=0。
答案對1e9+7取模。

輸入描述:

輸入一個n
n <= 1018

輸出描述:

輸出答案對1e9+7取模
示例1

輸入

3

輸出

6

題解:

我們很容易發現這個 01 串中,所有2^n個數字中,0 1 數量是對等的

此時我們假設第 i 位爲 0,那麼前面有 i-1 位  後面有 n-i 位

那麼前面有2^(i-1)個數,每一個數字有 i-1 個 0 或者 1 ,那麼 1 可以出現 ( i - 1 ) * 2^( i - 1) / 2 次,即 ( i - 1 ) * 2^( i - 2)

此時已經組成了 1 0 串了(逆序正序其實都是一樣的),第 i 位後面還有 2^(n-i) 個數,根據乘法原理得到 ( i - 1 ) * 2^( i - 2) * 2^(n-i)

化簡得到第 i 位爲 0 的時候滿足條件的個數了 ( i - 1 ) * 2^( n - 2)

求個和得到 n*( n - 1 ) * 2^( n - 3)

快速冪搞一下即可


#include<stdio.h>
#define LL long long
#define mod 1000000007

LL fast_pow(LL a,LL b){
    LL ans=1;
    while(b){
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

int main()
{
    LL n;
    //freopen("in.txt","r",stdin);
    while(scanf("%lld",&n)!=EOF)
    {
        if(n<=1)       printf("0\n");
        else if(n==2)  printf("1\n");
        else printf("%lld\n",n%mod*((n-1)%mod)%mod*fast_pow(2,n-3)%mod);
    }
    return 0;
}

D題:

題目描述

106號房間共有n名居民, 他們每人有一個重要度。房間的門上可以裝若干把鎖。假設共有k把鎖,命名爲1k。每把鎖有一種對應的鑰匙,也用1k表示。鑰匙可以複製併發給任意多個居民。每個106房間的居民持有若干鑰匙,也就是1k的一個子集。如果幾名居民的鑰匙的並集是1k,即他們擁有全部鎖的對應鑰匙,他們都在場時就能打開房門。新的陸戰協定規定,一組居民都在場時能打開房門當且僅當他們的重要度加起來至少爲m。問至少需要給106號房間裝多少把鎖。即,求最小的k,使得可以適當地給居民們每人若干鑰匙(即一個1k的子集),使得任意重要度之和小於m的居民集合持有的鑰匙的並集不是1k,而任意重要度之和大於等於m的居民集合持有的鑰匙的並集是1k

輸入描述:

第一行兩個整數n和m,0<n<21,0<m<1000000001。
第二行n個整數表示居民們的重要度。
重要度在[1,1000000000]之間。

輸出描述:

一個整數表示最少需要多少把鎖。
示例1

輸入

4 3
1 1 1 1

輸出

6

說明

106號房共有4名居民,只有3人在場時才能打開門。這時共需6把鎖。

題意:

中文題,自己看

題解:

答案就是:1~n 的滿足下面條件的子集數量之和,設爲 x

這些人的重要度之和小於 m ,但是加入任意一個人都會使得重要度之和大於等於 m

簡要證明:

易知這 x 子集都至少缺少某一把鑰匙,而任意兩個子集缺的鑰匙不能是同一把,如果缺同一把就不能滿足加入任意一個人使得這個子集重要度之和大於等於 m 且剛好有全部的鑰匙

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

int a[30],n,m;
LL dfs(int now,LL cnt,LL t)
{
    if(t==-1&&cnt+a[now+1]>=m) return 1;
    if(cnt+t>=m)  return 1;
    if(now>n) return 0;

    LL ans=0;
    if(cnt+a[now]<m) ans+=dfs(now+1,cnt+a[now],t);
    ans+=dfs(now+1,cnt,t==-1?a[now]:t);
    return ans;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        LL num=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),num+=a[i];
        if(num<m) {puts("1");continue;}
        sort(a+1,a+1+n);a[n+1]=0;
        LL ans=dfs(1,0,-1);
        printf("%lld\n",ans);
    }
    return 0;
}


E題:

題目描述

對於一個數字串 s,若能找到一種將其分成左右兩個非空部分 s1,s2 的方案,使得:
    1、s1,s2 均無前導零
    2、存在兩個正整數 a,b,使得 b 整除 a,且 a/b=s1, a*b=s2
那麼我們記這是一個合法的分法。特別地,如果一個串有兩個或更多個不同的合法的分法,那麼我們稱這個數字串是雙拆分數字串。
給定一個 n,要求構造一個長度恰爲 n 的雙拆分數字串。如果無解,輸出 -1。

輸入描述:

輸入僅一行一個正整數 n(1 <= n <= 300)。

輸出描述:

僅一行一個數字串或者 -1。
示例1

輸入

8

輸出

24419764


題意:

讓你輸出一個長度爲n的數字,這個數字有兩種或者更多的上敘的分解方法

題解:

分析得到n<=3無解

n = 4 : 1144 是一個合法解

(s1 =  1,s2 = 144,a = 12,b = 12)

(s1 = 11, s2 =  44,a = 22,b =  2)

n = 5 : 16400 是一個合法解

(s1 =  1,s2 = 6400,a = 80,b = 80)

(s1 = 16, s2 =  400,a = 80,b =  5)

之後 n = 6,7,8,,,,只需要將上面的 b 乘以 10 即可,輸出即爲答案


#include<stdio.h>

int main()
{
    int n;
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        if(n<=3) puts("-1");
        else if(n&1){
            printf("16400");
            for(int i=6;i<=n;i++) printf("0");
            puts("");
        }
        else{
            printf("1144");
            for(int i=5;i<=n;i++) printf("0");
            puts("");
        }
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章