牛客暑期ACM多校 第七场

链接:https://www.nowcoder.com/acm/contest/145/C
来源:牛客网

C .题目描述

A binary string s of length N = 2n is given. You will perform the following operation n times :

- Choose one of the operators AND (&), OR (|) or XOR (^). Suppose the current string is S = s1s2...sk. Then, for all , replace s2i-1s2i with the result obtained by applying the operator to s2i-1 and s2i. For example, if we apply XOR to {1101} we get {01}.

After n operations, the string will have length 1.

There are 3n ways to choose the n operations in total. How many of these ways will give 1 as the only character of the final string.

输入描述:

The first line of input contains a single integer n (1 ≤ n ≤ 18).

The next line of input contains a single binary string s (|s| = 2
n
). All characters of s are either 0 or 1.

输出描述:

Output a single integer, the answer to the problem.
示例1

输入

复制
2
1001

输出

复制
4

说明

The sequences (XOR, OR), (XOR, AND), (OR, OR), (OR, AND) works.

题意 : 你有 3 种操作,每次处理相邻的两个字符,问最终会有多少种操作得到 1

思路分析 : 直接一个爆搜即可,稍加一点剪枝,可以水过去

题解给的正解是,先暴力处理一下最后4种状态,即 2^16 ,下面再搜的时候复杂度会降为 3^(n - 4)

代码示例 :

using namespace std;
#define ll long long
const int maxn = 3e5+5;
const int mod = 1e9+7;
const double eps = 1e-9;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;

int n, len;
char s[maxn];
int sum = 0;
int pp[30], arr[20][maxn];

void dfs(int c, int len){
    if (len == 1) {
        if (arr[c][1]) sum++;
        return;
    }
    
    for(int i = 0; i < 3; i++){
        if (i == 0) {
            int num = 0;
            for(int j = 1; j <= len; j += 2){
                arr[c+1][(j+1)/2] = arr[c][j]&arr[c][j+1];
                if (arr[c+1][(j+1)/2]) num++;
            }
            dfs(c+1, len/2);
        }
        else if (i == 1){
            int num = 0;
            for(int j = 1; j <= len; j += 2){
                arr[c+1][(j+1)/2] = arr[c][j]|arr[c][j+1];
                if (arr[c+1][(j+1)/2]) num++;
            }
             dfs(c+1, len/2);
        }
        else {
            int num = 0;
            for(int j = 1; j <= len; j += 2){
                arr[c+1][(j+1)/2] = arr[c][j]^arr[c][j+1];
                if (arr[c+1][(j+1)/2]) num++;
            }
            dfs(c+1, len/2);
        }
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    pp[0] = 1;
    for(int i = 1; i <= 25; i++) pp[i] = pp[i-1]*2;
    scanf("%d%s", &n, s+1);
    len = 1;
    for(int i = 1; i <= n; i++) len *= 2;
    for(int i = 1; i <= len; i++) arr[1][i] = s[i]-'0';
    
    dfs(1, len);
    printf("%d
", sum);
      
    return 0;
}

链接:https://www.nowcoder.com/acm/contest/145/E
来源:牛客网

E .题目描述

You love doing graph theory problems. You've recently stumbled upon a classical problem : Count the number of 4-cliques in an undirected graph.

Given an undirected simple graph G, a 4-clique of G is a set of 4 nodes such that all pairs of nodes in this set are directly connected by an edge.

This task would be too easy for you, wouldn't it? Thus, your task here is to find an undirected simple graph G with exactly k 4-cliques. Can you solve this task?

输入描述:

The first line of input contains a single integer k (1 ≤ k ≤ 10
6
).

输出描述:

On the first line, output two space-separated integers, n, m (1 ≤ n ≤ 75, 1 ≤ m ≤ n * (n - 1) / 2). On the next m lines, output two space-separated integers denoting an edge of the graph u, v (1 ≤ u, v ≤ n), where u and v are the endpoints of the edge.

Your graph must not contain any self-loops or multiple edges between the same pair of nodes. Any graph that has exactly k 4-cliques and satisfies the constraints will be accepted. It can be proven that a solution always exist under the given constraints.
示例1

输入

复制
1

输出

复制
4 6
1 2
1 3
1 4
2 3
2 4
4 3

说明

In the sample, the whole graph is a 4-clique.

题意 :输入一个K,代表要求你构造的图内团的大小为 4 的个数

思路分析 : 团的定义是什么:图中大小为4的点彼此之间互相连通,即大小为 4 的完全子图,我们首先可以用一些点两两之间彼此连边,此时得到的完全图是C(n, 4),但此时并不一定刚好等于 K ,因此可以最后余下几个点,用这几个点去和原先的点去连边, 假设某一个点练了X条边,则新产生的团为4 的个数为 C(X, 3),这个地方的处理可以用一个背包即可。 C(a, 3)+ C(b, 3)+ C(c, 3)+ C(d, 3)+ C(e, 3) = num

代码示例 :

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5+5;

int dp[10][maxn], path[10][maxn];
int C[75][5];
int num;
int n, m, cha;
void init() {
    for(int i = 3; i <= 70; i++){
        int x = i*(i-1)*(i-2);
        C[i][3] = x/6;
        
        int x2 = i*(i-1)*(i-2)*(i-3);
        C[i][4] = x2/24;
    }
    
    for(int i = 4; i <= 70; i++){ 
        if (C[i][4] > num) break;
        n = i;    
    } 
    cha = num-C[n][4];
    
    dp[0][0] = 1;
    for(int i = 1; i <= 5; i++){
        dp[i][0] = 1;
        for(int j = cha; j >= 0; j--){
            for(int k = 3; k <= n; k++){
                if (dp[i][j]) continue;
                if (C[k][3] > j) break;
                if (dp[i-1][j-C[k][3]]) dp[i][j] = 1, path[i][j] = k;
            }
        }
    }
}


int f[10];
bool cmp(int a, int b){
    return a>b;
}
int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    
    cin >> num;     
    init();
    
    m = n*(n-1)/2;
    int an = n;
    for(int i = 5; i >= 1; i--){
        f[i] = path[i][cha];
        cha = cha - C[f[i]][3];
        m += f[i];
        if (f[i]) an++;
    }
    sort(f+1, f+6, cmp); 
    
    printf("%d %d
", an, m);
    for(int i = 1; i <= n; i++){
        for(int j = i+1; j <= n; j++){
            printf("%d %d
", i, j);
        }
    }
    for(int i = 1; i <= 5; i++){
        for(int j = 1; j <= f[i]; j++){
            printf("%d %d
", n+i, j);
        }
    }
    return 0;
}

链接:https://www.nowcoder.com/acm/contest/145/J

J . 题目描述

You have a n * m grid of characters, where each character is an English letter (lowercase or uppercase, which means there are a total of 52 different possible letters).

A nonempty subrectangle of the grid is called sudoku-like if for any row or column in the subrectangle, all the cells in it have distinct characters.

How many sudoku-like subrectangles of the grid are there?

输入描述:

The first line of input contains two space-separated integers n, m (1 ≤ n, m ≤ 1000).

The next n lines contain m characters each, denoting the characters of the grid. Each character is an English letter (which can be either uppercase or lowercase).

输出描述:

Output a single integer, the number of sudoku-like subrectangles.
示例1

输入

复制
2 3
AaA
caa

输出

复制
11

说明

For simplicity, denote the j-th character on the i-th row as (i, j).

For sample 1, there are 11 sudoku-like subrectangles. Denote a subrectangle
by (x
1
, y
1
, x
2
, y
2
), where (x
1
, y
1
) and (x
2
, y
2
) are the upper-left and lower-right coordinates of the subrectangle.

The sudoku-like subrectangles are (1, 1, 1, 1), (1, 2, 1, 2), (1, 3, 1, 3), (2, 1, 2, 1), (2, 2, 2, 2), (2, 3, 2, 3), (1, 1, 1, 2), (1, 2, 1, 3), (2, 1, 2, 2), (1, 1, 2, 1), (1, 3, 2, 3).
示例2

输入

复制
4 5
abcde
fGhij
klmno
pqrst

输出

复制
150

说明

For sample 2, the grid has 150 nonempty subrectangles, and all of them are sudoku-like.

题意 : 一个 n*m 的矩阵,求矩阵中子矩阵的个数,要求子矩阵中每行每列都没有相同的字母,求子矩阵的个数
思路分析 :
  首先预处理两个东西, le[i][j] 表示 从点 (i, j) 向左最多可以延伸的单位 ,up[i][j] 表示从点 (i, j) 最多可以向上延伸的单位
  len[i] 记录的是第 i 列的最大可以向上延伸的单位
代码示例 :

ll n, m;
char s[1005][1005];
ll mp[1005][1005];
ll up[1005][1005], le[1005][1005];

void init() { 
    for(ll j = 1; j <= m; j++){ // 列
        ll num = 0; ll len = 0;
        for(ll i = 1; i <= n; i++){ // 行
            if (!(num & ((1ll)<<mp[i][j]))) {
                num = num|((1ll)<<mp[i][j]);
                len++;
            }
            else {
                for(ll k = i-up[i-1][j]; k <= i-1; k++){
                    if (mp[k][j] == mp[i][j]) break;
                    len--;
                    ll x = (1ll)<<mp[k][j];
                    x = ~x;
                    num &= x;
                }
            }
            up[i][j] = len;
        }
    }
    
    for(ll i = 1; i <= n; i++){
        ll num = 0, len = 0;
        for(ll j = 1; j <= m; j++){
            if (!(num & (1ll)<<mp[i][j])){
                num = num|((1ll)<<mp[i][j]);
                len++;
            }
            else {
                for(ll k = j-le[i][j-1]; k <= j-1; k++){
                    if (mp[i][k] == mp[i][j]) break;
                    len--;
                    ll x = (1ll)<<mp[i][k];
                    x = ~x;
                    num &= x;
                }
            }
            le[i][j] = len;
        }
    }
}

ll ans = 0;
ll len[100];
void solve() {
    for(int j = 1; j <= m; j++){
        memset(len, 0, sizeof(len));
        for(int i = 1; i <= n; i++){
            for(int k = 0; k < le[i][j]; k++){
                len[k] = min(len[k]+1, up[i][j-k]);
                if (k) len[k] = min(len[k], len[k-1]);
                ans += len[k];
            }    
            for(int k = le[i][j]; k <= 55; k++) len[k] = 0;
        }
    }
    printf("%lld
", ans);
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    cin >> n >> m;
    for(ll i = 1; i <= n; i++){
        scanf("%s", s[i]+1);
    }
    for(ll i = 1; i <= n; i++){
        for(ll j = 1; j <= m; j++){
            if (s[i][j] >= 'a' && s[i][j] <= 'z') mp[i][j] = s[i][j]-'a';
            else mp[i][j] = s[i][j]-'A'+26;
        }
    }    
    init();     
    solve();
    return 0;
}
/*
4 4
afcd
bcda
dddd
abdc
*/

 关于 le[i][j] 和 up[i][j] 两个数组,网上看到一个比较好的做法,很简便

    for(int i = 1; i <= n; i++){
        memset(pos, 0, sizeof(pos));
        for(int j = 1; j <= m; j++){
            L[i][j] = min(L[i][j-1] + 1, j - pos[s[i][j]]);
            pos[s[i][j]] = j;
        }
    }

    for(int j = 1; j <= m; j++){
        memset(pos, 0, sizeof(pos));
        for(int i = 1; i <= n; i++){
            U[i][j] = min(U[i-1][j] + 1, i - pos[s[i][j]]);
            pos[s[i][j]] = i;
        }
    }

 le[i][j] 的值来源于两种:一种是左边的值 le[i][j-1] + 1, 另一种是与其是同一个字母的时候两者间的距离,很简洁

东北日出西边雨 道是无情却有情
原文地址:https://www.cnblogs.com/ccut-ry/p/9461711.html