2019牛客国庆集训派对day1

2019牛客国庆集训派对day1

// 2019.10.1 国庆练习赛
// 比赛链接:2019牛客国庆集训派对day1
// 虽然我国庆七天溜回家了,队友还是督促我好好打比赛...

// 毕竟现场赛也没几天了,好好练习哈

A 全 1 子矩阵

题目大意

判断矩阵是否存在子矩阵满足 \(x_1 \le x \le x_2, y_1 \le y \le y_2\) 内全是1,其他地方为0。

水题,暴力判断一下即可。

AC代码

#include<iostream>
#include<cstdio>
using namespace std;

char mat[15][15];
int n, m;
int x1, y1, x2, y2;

void check() {
    for(int i=x1;i<=x2;i++) {
        for(int j=y1;j<=y2;j++) {
            if(mat[i][j]=='1') {
                mat[i][j] = '0';
            }else {
                printf("No\n");
                return;
            }
        }
    }
    
    for(int i=0;i<n;i++) {
        for(int j=0;j<m;j++) {
            if(mat[i][j]=='1') {
                printf("No\n");
                return;
            }
        }
    }
    printf("Yes\n");
    
}

int main() {
    while(scanf("%d %d", &n, &m)!=EOF) {
        for(int i=0;i<n;i++)
            scanf("%s", mat[i]);
        
        x1 = y1 = x2 = y2 = -1;
        for(int i=0;i<n&&x1==-1;i++) {
            for(int j=0;j<m;j++) {
                if(mat[i][j]=='1') {
                    x1 = i, y1 = j;
                    break;
                }
            }
        }
        
        for(int i=n-1;i>=0&&x2==-1;i--) {
            for(int j=m-1;j>=0;j--) {
                if(mat[i][j]=='1') {
                    x2 = i, y2 = j;
                    break;
                }
            }
        }
        
        if(x1==-1||x2==-1) {
            printf("No\n");
            continue;
        }
        
        check();
    }
    return 0;
}

B 组合数

题目大意

给定n 与 k, 求 \(min(C_n^k, 10^{18})\)

\(0 ≤ k ≤ n≤10^9\) , 至多 \(10^5\) 组数据。

思路

组合数 \(C_{100}^{50}\) 已经远超 \(10^{18}\),那么显然可以考虑使用 O(n) 的递推式求组合数,即便 \(10^5\) 组数据也跑的过去。

再次复习一遍组合数递推式

\[C_n^k = \frac {n-k+1} {k} C_n^{k-1}\]

AC代码

折腾半天才A,注意到 \(C_n^{n-k} = C_n^k\) , 先 k = min(k, n-k),保证计算过程是单调的。

double A不过去,又要用到无敌的 __int128 。赛后发现可以用 long double 保证精度。

#include<cstdio>
using namespace std;
typedef long long ll;
//typedef __int128 i128;
const ll INF = 1e18;

ll cal(ll n, ll k) {
    if(k>n/2) k = n - k;
    // i128 res = 1;
    long double res = 1.0;
    for(ll i=1;i<=k;i++) {
        res = res*(n-i+1)/i;
        if(res>INF) return INF; 
    }
    return (ll)res;
}

int main() {
    ll n, k;
    while(scanf("%lld %lld", &n, &k)!=EOF) {
        printf("%lld\n", cal(n, k));
    }
    return 0;
}

优雅简洁的Python写法:

while True:
    try:
        n, k = map(int, input().split())
        k = min(k, n-k)
        
        ans = 1
        for i in range(1, k+1):
            ans = ans * (n-i+1) // i
            if ans>10**18 :
                ans = 10**18
                break   
        print(ans)
    except EOFError:
        break

E Numbers

题目大意

有 n 个范围在 [0, 99] 之间的整数,现在他们相连写成了一列数,问原来的数组有多少种可能。

思路

原以为是 dp,写了一半不对劲,改写 dfs ,没搜出样例,溜了。。。

// 原来是dfs时 pos==len 才++ans。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring> 
using namespace std;
 
char s[55];
bool vis[100];
int len;
int ans;
 
void dfs(int pos) {
    if(pos>len) return;
    if(pos==len) {
        ++ans;
        return;
    }
    int n = s[pos]-'0';
    int nn = n*10;
    if(!vis[n]) {
        vis[n] = 1;
        dfs(pos+1);
        vis[n] = 0;
    }
    if(nn && pos<len-1) nn += s[pos+1]-'0';
    if(nn>=10 && !vis[nn]) {
        vis[nn] = 1;
        dfs(pos+2);
        vis[nn] = 0;
    }
}
 
int main() {
    while(scanf("%s", s)!=EOF) {
        memset(vis, 0, sizeof(vis));
        len = strlen(s);
        ans = 0;
        dfs(0);
        printf("%d\n", ans);
    }
    return 0;
}

F 4 Buttons

题目大意

Bobo一开始位于平面上的原点 (0,0),有四种操作:向右最多移动a步,向上最多移动b步,向左最多移动c步,向下最多移动d步。问执行 n 次操作可以到达多少个不同的点。

思路

只进行一步的操作很简单,分别可以到达坐标轴上离原点最远的 (a, 0), (0, b), (-c, 0), (0, -d)。故包含原点在内共有 1 + a + b + c + d 个点。

多步的话,我们先只看第一象限内的情况:

第一次操作到达x轴上区间 [1, a],第2次到第n次竖直方向上能到达 [1, (n-1)b)] ,共 a*(n-1)b个点;

两次操作到达x轴上区间 [a+1, 2a], 第2次到第n次竖直方向上能到达 [1, (n-2)b)] ,共 a*(n-2)b个点;

···

n-1次操作到达x轴上区间 [(n-2)a+1, (n-1)a], 第n-1次到第n次竖直方向上能到达 [1, b] ,共 a*b个//点;

// n 次操作到达x轴上最远的a个点 [(n-1)a+1, na]。

所以答案很简单, 1 + n(a + b + c + d) + n(n-1)/2 * (ab + bc + cd + ad) 。

AC代码

#include<cstdio>
using namespace std;
const int mod = 1e9+7;
typedef long long ll;
ll n, a, b, c, d;
int main() {
    while(scanf("%lld %lld %lld %lld %lld", &n, &a, &b, &c, &d)!=EOF) {
        ll ans = 1 + (a+b+c+d)%mod * n%mod + n*(n-1)/2 %mod
        *((a*b%mod+ b*c%mod + c*d%mod + a*d%mod)%mod)%mod;
        printf("%lld\n", ans%mod);
    }
    return 0;
}

H 有向图

题目大意

数学题,待补,我的锅。

猜你喜欢

转载自www.cnblogs.com/izcat/p/11618652.html