Week14

路径计数

有一个n×n的网格,有些格子是可以通行的,有些格子是障碍。

一开始你在左上角的位置,你可以每一步往下或者往右走,问有多少种走到右下角的方案。

由于答案很大,输出对109+7109+7取模的结果。

输入格式

第一行一个正整数n。

接下来n行,每行n个正整数,11表示可以通行,00表示不能通行。

输出格式

一个整数,表示答案。

样例输入
3
1 1 1
1 0 1
1 1 1
样例输出
2
数据规模

对于100%100%的数据,保证2≤n≤100,左上角右下角都是可以通行的。

代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int m = 1000000007;
int n;
int g[maxn][maxn];
int dp[maxn][maxn];

int main(){
    memset(g, 0, sizeof(g));
    memset(dp, 0, sizeof(dp));
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            scanf("%d", &g[i][j]);
        }
    }
    dp[1][1] = 1;
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            if(g[i][j] == 1){
                if(i == 1 && j == 1)
                    continue;
                dp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % m;
            }
        }
    }
    // for(int i = 0; i <= n; ++i){
    //     for(int j = 0; j <= n; ++j){
    //         printf("%d ", dp[i][j]);
    //     }
    //     printf("\n");
    // }
    printf("%d", dp[n][n]);
    //system("pause");
    return 0;
}

最大和上升子序列

给定一个长度为 n 的数组 a1,a2,…,an,问其中的和最大的上升子序列。也就是说,我们要找到数组 p1,p2,…,pm,满足 1≤p1<p2<⋯<pm≤n 并且 ap1<ap2<⋯<apm,使得ap1+ap2+⋯+apm最大。

输入格式

第一行一个数字 n。

接下来一行 n个整数 a1,a2,…,an

输出格式

一个数,表示答案。

样例输入
6
3 7 4 2 6 8
样例输出
21
数据规模

所有数据保证 1≤n≤1000,1≤ai≤105

代码
#include<bits/stdc++.h>
using namespace std;
int num[1005], n;
int dp[1005];  //表示所有以 num[i] 结尾的最大的上升子序列

int main(){
    scanf("%d", &n);
    for(int i = 0; i < n; ++i){
        scanf("%d", &num[i]);
        dp[i] = num[i];
    }
    int ans = -1;
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < i; ++j){
            if(num[i] > num[j])
                dp[i] = max(dp[j] + num[i], dp[i]);
        }
        ans = max(ans, dp[i]);
    }
    printf("%d", ans);
    //system("pause");
    return 0;
}

加一

给定一个整数 n。你需要对它做 m 次操作。在一次操作中,你要将这个数的每一位 d 替换成 d+1。比如,19121912 在进行一次操作后将变成 2102321023。

请求出整数 n 进行了 m 次操作后的长度。答案可能很大,输出对 109+7109+7 取模后的结果。

输入格式

第一行一个整数 t,表示测试单元的个数。

接下来 t 行,每行有两个整数 n 和 m,表示最初的数字和进行多少次操作。

输出格式

对于每个测试单元输出最终数字的长度,答案对 109+7 取模。

样例输入
5
1912 1
5 6
999 1
88 2
12 100
样例输出
5
2
6
4
2115
数据规模

所有数据保证 1≤t≤2⋅105,1≤n≤109,1≤m≤2⋅105。

代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
const int m = 1e9 + 7;
int n, ans, k, dp[maxn][10];  //dp[i][j]代表对 j 操作 i 次可以到达的长度
char s[15];

void init(){
    for(int i = 0; i < 10; ++i)
        dp[0][i] = 1;  //对任何数操作一次都是 1 长度
    for(int i = 0; i < maxn; ++i){  //每次操作都加 1
        for(int j = 1; j < 10; ++j){
            dp[i + 1][j - 1] = dp[i][j];
        }
        dp[i + 1][9] = (dp[i][0] % m + dp[i][1] % m) % m;   //要对 9 特殊处理
    }
    // for(int i = 0; i < maxn; ++i){
    //     for(int j = 1; j < 10; ++j)
    //         cout << dp[i][j] << " ";
    //     cout << endl;
    // }
    return;
}

int main(){
    scanf("%d", &n);
    init();
    for(int j = 0; j < n; ++j){
        scanf("%s%d", &s, &k);
        int len = strlen(s);
        ans = 0;
        for(int i = 0; i < len; ++i){
            ans = (ans + dp[k][s[i] - '0']) % m;
        }
        printf("%d\n", ans % m);
    }
    //system("pause");
    return 0;
}

跳跳

平面上给定了一些整点(横纵坐标均为整数的点),被称为 “魔法阵”。魔法少女派派想要在各魔法阵之间传送,每一次传送,她将使用下面的方式:

  1. 刚开始,派派已经位于某传送阵之上;
  2. 如果派派掌握一种魔法 (A,B),其中 A,B均为整数。使用一次这个魔法可以让派派从任意整点 (X,Y) 瞬间移动至 (X+A,Y+B);
  3. 选择一种魔法并开始传送,在一次传送过程中可以使用多次该魔法,但在抵达下一个传送阵之前仅能使用这一种魔法

问派派至少需要掌握多少种魔法,才能在从任意魔法阵直接传送到任意魔法阵?

输入格式

第一行一个整数 N。

接下来一行 N 行,每行包含两个整数 Xi,Yi, 表示每个魔法阵的坐标。

输出格式

一个数,表示答案。

样例1输入
3
1 1
4 5
1 4
样例1输出
6

解释: 任务是从 (1,1)(1,1) 传送至 (4,5)(4,5) 以及 (1,4)(1,4) 、从 (4,5)(4,5) 传送至 (1,1)(1,1) 以及 (1,4)(1,4) 、从 (1,4)(1,4) 传送至 (1,1)(1,1) 以及 (4,5)(4,5) 。

注意你不能使用 (0,3)+(3,1)(0,3)+(3,1) 的魔法从 (1,1)(1,1) 到达 (4,5)(4,5)。因为每次移动,你只能使用一种魔法。

当然,你可以学习 (0,1)(0,1),那样的话,从 (1,1)(1,1) 到达 (1,4)(1,4) 则需要使用 33 次 (0,1)(0,1) 魔法了。

样例2输入
3
1 1
2 2
1000000000 1000000000
样例2输出
2
数据规模
  • N∈[10,500]
  • Xi,Yi∈[0,109], 但保证坐标之间两两不同
代码
#include<bits/stdc++.h>
using namespace std;
int n, ans = 0;
pair<long, long> a[505];
set<pair<long long, long long>> m;

long long gcd(long long x, long long y){
    if(x < y){
        int t = x;
        x = y;
        y = t;
    }
    int z = y;
    while(x % y != 0){
        z = x % y;
        x = y;
        y = z;
    }
    return z;
    // return y ? gcd(y, x % y) : x;
}

int main(){
    //memset(m, 0, sizeof(m));
    scanf("%d", &n);
    for(int i = 0; i < n; ++i){
        scanf("%d%d", &a[i].first, &a[i].second);
    }
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < n; ++j){
            if(i == j)
                continue;
            long long x = a[i].first - a[j].first;
            long long y = a[i].second - a[j].second;
            long long k = gcd(x, y);
            //cout << "k: " << k << " x / k " << x/k << " y / k " << y/k << endl;
            m.insert({x/k, y/k});
        }
    }
    printf("%d", m.size() * 2);
    //system("pause");
    return 0;
}

异或和或

对于一个长度为 n 的0101序列 a1,a2,…,an

你可以执行以下操作任意多次:

  • 选择两个下标 1≤i,j≤n(i≠j)
  • 记x=ai xor aj, y=ai or aj , 其中 xorxor 表示按位异或 , oror 表示按位或。
  • 然后令 ai=x,aj=y 或 ai=y,aj=x。

给定两个0101序列 s,t, 请你判断是否可以通过有限次(可以为00次)操作将序列 s 变为 t。

输入格式

第一行一个整数 t , 表示数据的组数(1≤t≤103)。接下来 t 组数据:

每组第一行一个0101字符串 s(1≤|s|≤103),每组第二行一个0101字符串 t(1≤|t|≤103)。

注意:|s|可能不等于 |t|。

输出格式

如果可以通过有限次(可以为00次)操作将序列 s 变为 t , 输出 YES , 否则输出 NO

样例输入
2
001
011
11
101
样例输出
YES
NO
样例解释

第一组数据选择 i=2,j=3, 那么 x=1,y=1, 接着令 ai=x,aj=y即可得到 t 序列。

第二组数据 |s|=2,|t|=3显然无法满足要求。

代码
#include<bits/stdc++.h>
using namespace std;
int n;
char s1[1005], s2[1005];

int main(){
    scanf("%d", &n);
    while(n--){
        scanf("%s%s", s1, s2);
        if(strlen(s1) != strlen(s2)){
            printf("NO\n");
            continue;
        }
        int a = 0, b = 0;
        for(int i = 0; i < strlen(s1); ++i){  //规律是只要一个字符串中有 1,那么就可以生成任何的串(除全零外)
            if(s1[i] - '0' == 1)
                a++;
            if(s2[i] - '0' == 1)
                b++;
        }
        if((a == 0 && b != 0) ||(a != 0 && b == 0) )
            printf("NO\n");
        else 
            printf("YES\n");
    }
    //system("pause");
    return 0;
}

01序列

我们称一个字符串为好字符串,指这个字符串中只包含01

现在有一个好字符串,求这个字符串中1恰好出现k次的子串有多少个。

输入格式

第一行给出一个数字k,表示子串中1的个数。

第二行给出好字符串。

输出格式

输出一个整数,表示好字符串中有多少个符合条件的子串

数据范围

0≤k≤106, |s|≤106

样例输入1
1
1010
样例输出1
6
样例输入2
2
01010
样例输出2
4
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int k;
ll a = 0, res = 0, ans = 0;
string s;
map<int, int> m;
int num[maxn] ={0}, pre[maxn] = {0};


int main(){
    m[0]++;
    scanf("%d", &k);
    cin >> s;
    if(k == 0){
        ans = 0;
        s += '1';
        for(int i = 0; i <= s.length(); ++i){
            if(s[i] == '0')
                ans++;
            else{
                res += (ans + 1) * ans / 2;
                ans = 0;
            }
        }
    }
    else
    {
        for(int i = 1; i <= s.length(); ++i){
        num[i] = s[i - 1] - '0';
        pre[i] = num[i] + pre[i - 1];
        m[pre[i]]++;
        }
        ans = 0;
        // for(auto t : m){
        //     cout << t.first << " " << t.second << endl;
        // }
        // for(int i = 0; i <= s.length(); ++i)
        //     cout << "pre[" << i << "]: " << pre[i] << endl;
        while(m[k + ans] != 0){
            //cout << ans << " " << ans + k << " " << pre[ans] << " " << pre[ans + k] << endl;
            res += (m[ans] * m[ans + k]);
            //cout << res << endl;
            ans++;
        }
    }
    cout << res << endl;
    //system("pause");
    return 0;
}

出栈序列判断

现在有一个栈,有 n 个元素,分别为 1,2,…,n。我们可以通过 pushpop 操作,将这 n 个元素依次放入栈中,然后从栈中弹出,依次把出栈的元素写下来得到的序列就是出栈序列。

比如 n=3,如果执行 push 1, push 2, pop, push 3, pop, pop,那么我们 pop 操作得到的元素依次是 2,3,12,3,1。也就是说出栈序列就是 2,3,12,3,1。

现在给定一个合法的出栈序列,请输出一个合法的由 pushpop 操作构成的操作序列。这里要求 push 操作一定是按 1,2,…,n的顺序。

输入格式

第一行一个整数 n。接下来一行 n 个整数,表示出栈序列。

输出格式

输出 2n2 行,每行一个 pushpop 操作,可以证明一个出栈序列对应的操作序列是唯一的。

样例输入1
3
2 3 1
样例输出1
push 1
push 2
pop
push 3
pop
pop
样例输入2
5
1 3 5 4 2
样例输出2
push 1
pop
push 2
push 3
pop
push 4
push 5
pop
pop
pop
数据规模

对于 100%100% 的数据,保证 1≤n≤100000,输入一定是个合法的出栈序列。

代码
#include<bits/stdc++.h>
using namespace std;
int n, arr[100005], top, cur, cnt;  //top 记录栈顶元素
//stack<int> st;

int main(){
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
        scanf("%d", &arr[i]);
    top = 0;
    cur = 0;
    cnt = 1;
    for(int i = 0; i < 2 * n; ++i){
        if(top < arr[cur]){
            printf("push %d\n", cnt);
            top = cnt;
            cnt++;
        }
        else if(top == arr[cur]){
            printf("pop\n");
            cur++;
        }
        else{
            printf("pop\n");
            cur++;
        }
    }
    //system("pause");
    return 0;
}

序列维护

你有一个序列,现在你要支持几种操作:

  • insert x y,在从前往后的第x个元素后面插入y这个数。如果x=0,那么就在开头插入。
  • delete x,删除从前往后的第x个元素。
  • query k,询问从前往后数第k个元素是多少。
输入格式

第一行一个整数m,表示操作个数。

接下来m行,每行一个上面所述的操作。

输出格式

输出若干行,对于每个查询操作,输出答案。

样例输入
10
insert 0 1
insert 1 2
query 1
query 2
insert 0 3
query 1
delete 1
query 1
insert 1 4 
query 2
样例输出
1
2
3
1
4
数据规模

对于100%100%的数据,保证m≤103。

对于insert操作,保证1≤y≤109。

对于所有操作,保证位置不会超出当前序列的长度。

代码
#include<bits/stdc++.h>
using namespace std;
typedef struct node{
    int num;
    node* next;
}node;
node* head;
int n, a, b;
string s;

void insert(int index, int number){
    node* temp = new node;
    temp->num = number;
    node* cur = head;
    while(index--){
        cur = cur->next;
    }
    temp->next = cur->next;
    cur->next = temp;
}

void deleteNode(int index){
    node* cur = head;
    while(--index){
        cur = cur->next;
    }
    cur->next = cur->next->next;
    //delete(cur->next);
}

void query(int index){
    node* cur = head;
    while(index--){
        cur = cur->next;
    }
    printf("%d\n", cur->num);
}

int main(){
    node *cur = new node;
    cur->num = 0;
    cur->next = nullptr;
    head = cur;
    scanf("%d", &n);
    while(n--){
        cin >> s;
        if(s == "insert"){
            scanf("%d%d", &a, &b);
            insert(a, b);
        }
        else if(s == "delete"){
            scanf("%d", &a);
            deleteNode(a);
        } 
        else{
            scanf("%d", &a);
            query(a);
        }
    }
    //system("pause");
    return 0;
}

网格判断

您将获得一个 n×n 的网格,网格中每个正方形的颜色为黑色或白色。如果满足以下所有条件,则网格是正确的:

  • 每行的黑色方块数与白色方块数相同。
  • 每列的黑色正方形数与白色方块数相同。
  • 没有行或列具有 33 个及以上相同颜色的连续正方形。

给定网格,确定它是否正确。

输入格式

第一行一个数字 n(2≤n≤24), 并且数字 n 是偶数。

接下来 n 行,每行包含一个长度为n的由字符BW组成的字符串,代表网格正方形的颜色。

输出格式

如果网格正确,请打印数字 11 在一行上。否则,请打印数字 00 在一行上。

样例输入
4
WBBW
WBWB
BWWB
BWBW
样例输出
1
代码
#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n;
    cin >> n;
    vector<int>s(n + 1);
    vector<vector<char>>v(n + 1, vector<char>(n + 1));
    string str;
    for (int i = 1; i <= n; i++)
    {
        cin >> str;
        for (int j = 1; j <= n; j++)
        {
            v[i][j] = str[j - 1];
            s[j] = s[j - 1];
            if (str[j - 1] == 'W')s[j] += 1;
            if (j >= 4&&(s[j]==s[j-3]||s[j]-s[j-3]==3))
            {
                cout << 0 << endl;
                return 0;
            }
        }
        if (s[n] != n / 2)
        {
            cout << 0 << endl;
            return 0;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            s[j] = s[j - 1];
            if (v[j][i] == 'W')s[j] += 1;
            if (j >= 4 && (s[j] == s[j - 3] || s[j] - s[j - 3] == 3))
            {
                cout << 0 << endl;
                return 0;
            }
        }
        if (s[n] != n / 2)
        {
            cout << 0 << endl;
            return 0;
        }
    }
    cout << 1 << endl;
    return 0;
}

整齐的数组

Polycarp 有一个长度为 n 的数组 a1,a2,…,an(n 是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n) 使 ai 减去 k。

在 Polycarp 进行若干次操作后(可能 00 次),数组 a 中的所有数都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1−1。

输入格式

第一行一个整数 t,表示测试单元的个数。

接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a1,a2,…,an。

输出格式

对于每个测试单元输出单独一行一个整数 k (k≥1) —— Polycarp 能用来对数组进行操作的最大的数,或者 −1−1 —— 如果 k 能任意大的话。

样例输入
3
6
1 5 3 1 1 5
8
-1 0 1 -1 0 1 -1 0
4
100 -1000 -1000 -1000
样例输出
2
1
1100
数据规模

所有数据保证 1≤t≤10,4≤n≤40(n是偶数),−106≤ai≤106,并且 n 的总和不超过100。

代码
#include<bits/stdc++.h>
using namespace std;
int n, tn;
int v[50];

int gcd(int x, int y){
    if(x == 0)
        return y;
    if(y == 0)
        return x;
    int a = max(x, y);
    int b = x + y - a;
    while((a % b) != 0){
        int t = a % b;
        a = b;
        b = t;
    }
    return b;
}

int main(){
    scanf("%d", &n);
    while(n--){
        scanf("%d", &tn);
        for(int i = 0; i < tn; ++i)
            scanf("%d", &v[i]);
        int avgGcd = 0;
        for(int i = 0; i < tn; ++i){
            for(int j = 0; j < tn; ++j){
                avgGcd = gcd(v[i] - v[j], avgGcd);
                //cout <<v[i] << " " << v[j] << " " << avgGcd << endl;
            }
        }
        if(avgGcd != 0)
            printf("%d\n", ((avgGcd < 0) ? -avgGcd : avgGcd));
        else
            printf("-1\n");
    }
    //system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值