牛客周赛 Round 63(构造、组合数、线性基)

发布于:2024-10-16 ⋅ 阅读:(5) ⋅ 点赞:(0)

牛客周赛 Round 63(构造、组合数、线性基)

A. 小红的好数

按照题意判断即可。

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

int main() {

    string s;
    cin >> s;
    
    if(s.size() == 2 && s[0] == s[1]) cout << "Yes" << endl;
    else cout << "No" << endl;
	
	return 0;
}

B. 小红的好数组

枚举所有区间长度为 k 的子区间即可。

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

const int maxn = 2e5 + 5;
int a[maxn];

int main() {

	int n, k;
	cin >> n >> k;
	for(int i = 1; i <= n; i++) cin >> a[i];
	
	int res = 0;
	for(int x = 1; x+k-1 <= n; x++){ // 枚举左端点
		int cnt = 0;
		for(int i = 1; i <= k; i++){
			int l = x + i - 1, r = x + k - i; // 计算对称位置的下标
			if(l > r) break;
			if(a[l] != a[r]) cnt++;
		}		
		res += (cnt == 1);
	}
	
	cout << res << endl;
		
	return 0;
}

C. 小红的矩阵行走(简单思维题)

判断 ( 1 , 1 ) (1,1) (1,1) ( n , n ) (n, n) (n,n) 的路径中,是否存在一条路径满足“路径上有 n + m - 1 个 a [ 1 ] [ 1 ] a[1][1] a[1][1] ”。

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

const int maxn = 100 + 5;
int a[maxn][maxn];

int main() {

    int ncase;
    cin >> ncase;
    
    while(ncase--){
        int n, m;
        cin >> n >> m;
        int first = -1, x;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= m; j++){
                cin >> x;
                if(first == -1) first = x;
                a[i][j] = max(a[i-1][j], a[i][j-1]) + (x == first);
            }
        }
        if(a[n][m] == n + m - 1) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
    
	return 0;
}

D. 小红的行列式构造(构造、数学题)

看代码。

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

int main() {
    
    int x;
    cin >> x;
    
    if(x == -1){	// 一行列式等于 -1 的矩阵,下列通法不满足题目条件的一种情况。
        cout << "1 1 1" << endl;
        cout << "2 3 2" << endl;
        cout << "2 2 1" << endl;  
    }
    else{	// 全一矩阵加一个对角线矩阵
        cout << x+1 << " 1 1" << endl;
        cout << "1 2 1" << endl;
        cout << "1 1 1" << endl;        
    }
    
	return 0;
}

E. 小红的 red 计数(组合数)

核心思路,总方案数 - 受反转影响消失的red + 受反转影响出现的red。细节看代码。

设,每次查询,可以把 ( 1 , n ) (1,n) (1,n) 分割为 A、B、C三个区间。

在这里插入图片描述

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

const int maxn = 1e5 + 5;
ll r[maxn], e[maxn], d[maxn];
ll re[maxn], de[maxn], er[maxn], ed[maxn];
ll red[maxn], der[maxn];

int main() {

	int n, q;
	cin >> n >> q;
	string s;
	cin >> s;
	s = " " + s;

	for(int i = 1; i <= n; i++) {
		r[i] = r[i-1] + (s[i] == 'r');
		e[i] = e[i-1] + (s[i] == 'e');
		d[i] = d[i-1] + (s[i] == 'd');

		re[i] = re[i-1] + (s[i] == 'e' ? r[i-1] : 0);
		de[i] = de[i-1] + (s[i] == 'e' ? d[i-1] : 0);
		er[i] = er[i-1] + (s[i] == 'r' ? e[i-1] : 0);
		ed[i] = ed[i-1] + (s[i] == 'd' ? e[i-1] : 0);

		red[i] = red[i-1] + (s[i] == 'd' ? re[i-1] : 0);
		der[i] = der[i-1] + (s[i] == 'r' ? de[i-1] : 0);
	}

	int x, y;
	while(q--){
		cin >> x >> y;
		ll res = red[n];
		// A区间提供‘r’,B区间提供‘ed’的情况。  
		// 注意ed[y] - ed[x-1] 包含‘e’在 A区间,‘d’在 B区间的情况。减去这种情况,得到由 B区间提供的‘ed’,其他同理。
		res = res - r[x-1] * (ed[y] - ed[x-1] - e[x-1] * (d[y] - d[x-1]));
		res = res + r[x-1] * (de[y] - de[x-1] - d[x-1] * (e[y] - e[x-1]));
		
		// 由 B区间提供 re 的情况。
		res = res - (d[n] - d[y]) * (re[y] - re[x-1] - r[x-1] * (e[y] - e[x-1]));
		res = res + (d[n] - d[y]) * (er[y] - er[x-1] - e[x-1] * (r[y] - r[x-1]));
		
		// 由 B区间提供 red 的情况。
		res = res - (red[y] - red[x-1] - r[x-1] * (ed[y] - ed[x-1] - e[x-1] * (d[y] - d[x-1])) - re[x-1] * (d[y] - d[x-1]));
		res = res + (der[y] - der[x-1] - d[x-1] * (er[y] - er[x-1] - e[x-1] * (r[y] - r[x-1])) - de[x-1] * (r[y] - r[x-1]));
				
		cout << res << endl;
	}

	return 0;
}

F. 小红开灯(线性基)

构造一个 n 维的向量空间,把第 i 个灯出现的位置映射到一个二进制向量的第 i 维。

第 i 个灯和与其同行同列的灯构成一个空间向量 x,一共可以构造出 n 个向量。

同时,对正在开着的灯,构造一个向量tar。如果在向量集合 X 中,可以选出若干个向量 x 表示出 tar,即问题有解。

对于样例一,构造一个 3 维的向量空间,其集合 X 为:

x1: (1, 1, 1) // 第一个灯与第二、第三个灯同行同列
x2: (1, 1, 0) // 第二个灯与第一个灯同列
x3: (1, 0, 1) // 第三个灯与第一个灯同行

构造出的目标向量tar为:

tar: (1, 0, 0) // 只有第一个灯开着的

在 X 中选择 x1、x2 和 x3 可表示出 tar,即 tar = x1 ^ x2 ^ x3,即样例一有解,对1、2、3 三个灯都操作一次就好。

但,显然,对于更复杂的样例,我们不能轻易的判断出都要选择什么灯来操作。

这里,我们引入’基’ 的概念,对于 n 维向量空间,对每一维都创建一个’基‘,即选择若干个向量,通过异或,可以得到最高位在 i 为 1 的向量。

举个例子,在样例一中:

第一维的基y1为 (1, 1, 1),即 x1
第二维的基y2为 (0, 1, 0), 即 x1 ^ x3
第三维的基y3为 (0, 0, 1), 即 x1 ^ x2

此时,再看目标向量tar,tar 可由 y1 ^ y2 ^ y3 得到,即 (x1) ^ (x1^x3) ^ (X1^x2) = tar,对1、2、3 三个灯都操作(x1、x2、x3 都出现奇数次)。

这里,样例一可能比较特殊,我们把灯的初始状态修改为 001。

此时,tar = (1, 1, 0),通过 y1 ^ y3 可以得到,即 (x1) ^ (x1 ^ x2) ,即对灯 2 进行一次操作即可。

上述描述,只是为了方便大家了解线性基,深入学习移步其他大佬的教学贴。

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

struct liner_base{
	vector<bitset<101> > a;	// 表示每个维度的基向量 
	vector<vector<int> > b;		// 表示每个维度的元素集合 
	int cnt = 0;
	liner_base(){
		a.resize(101);
		b.resize(101);
	}
	int insert(bitset<101> x, int id){
		vector<int> v = {id};
		for(int i = 100; i >= 0; i--){	// 高位开始遍历 
			if(x[i]){	// 第 i 位为1 
				if(a[i].count() == 0){ // 该维度没有被表示,但是可以被当前集合表示 
					a[i] = x;
					b[i] = v;
					cnt++; 		// 统计可以表示几个维度 
					return 1; 	// 插入成功 
				}
				else{	// 当前维度已经被标示,并且 x 有该维度 
					x ^= a[i];	// 把 x 中该维度去除 
					for(auto item : b[i]) v.push_back(item);	// 元素集合合并 
				} 
			}
		}
		return 0; 
	}
	int size(){
		return cnt;
	} 
	vector<int> ask(bitset<101> x){
		vector<int> res;
		for(int i = 100; i >= 0; i--){
			if(x[i]){	// 同上 
				x ^= a[i];
				for(auto item : b[i]) res.push_back(item);
			}
		}
		if(x.count() != 0) res.clear();
		return res;
	}
};


int s[105], x[105], y[105];

map<int, vector<int> > m_x, m_y;

int main() {

    int n;
    cin >> n;
    char c;
    for(int i = 1; i <= n; i++){
    	cin >> c;
    	s[i] = c - '0';
	}
    for(int i = 1; i <= n; i++) cin >> x[i] >> y[i];
    
	for(int i = 1; i <= n; i++) m_x[x[i]].push_back(i);
	for(int i = 1; i <= n; i++) m_y[y[i]].push_back(i);
    
    int count = 0;
    liner_base lb;
    for(int i = 1; i <= n; i++){
    	bitset<101> tmp;
		for(auto item : m_x[x[i]]) tmp[item] = 1;
		for(auto item : m_y[y[i]]) tmp[item] = 1;
		lb.insert(tmp, i); // 第i个点相关的灯组合成一个元素,插入线性基 
		count += s[i];
	}
	
	bitset<101> tar;
    for(int i = 1; i <= n; i++) if(s[i] == 0) tar[i] = 1;  // 所有没开的灯 
    
    if(count == n) cout << 0 << endl;
    else{
    	vector<int> res = lb.ask(tar);
    	if(res.empty()) cout << -1 << endl;	// 线性基不能表示tar 
    	else{
    		vector<int> ans(n+1);
			for(auto id : res) ans[id]++; 	// 统计第 id 个灯操纵了几次
			int sum = 0;
			for(int i = 1; i <= n; i++) sum += ans[i] % 2; // 统计所有奇数次的
			cout << sum << endl;
			for(int i = 1; i <= n; i++){
				if(ans[i] & 1){
					cout << i << " ";
				}
			}
		}
	}
    
	return 0;
}