2021牛客寒假算法基础集训营4

九峰与签到题
链接:https://ac.nowcoder.com/acm/contest/9984/A
来源:牛客网

题目描述

九峰正在准备一场毒瘤比赛,他是如此毒瘤以致于他想方设法降低通过率,他认为任意时间内通过率大于等于50%的题为签到题,现按照时间顺序给出整场比赛提交记录,请你输出哪些是签到题。

输入描述:
第一行输入两个整数m,n,表示提交记录个数和题目个数(m\leq 10^5,n\leq20)(m≤10
5
,n≤20)
接下来m行,每行输入一个正整数a_ia
i

和一个字符串op,表示题目序号和评测结果(a_i\leq n,op \epsilon { AC",UNAC"})(a
i

≤n,opϵ{‘‘AC",‘‘UNAC"})
输出描述:
从小到大输出签到题的序号,按空格分割。若没有则输出-1。
示例1
输入
复制
10 2
1 AC
2 AC
1 AC
1 UNAC
1 AC
2 AC
2 UNAC
1 UNAC
2 UNAC
1 UNAC
输出
复制
1 2
备注:
一道题没有任何提交时通过率认为是100%
若没有提交记录则被视为非签到题

符合条件的是出现过,但是没有一个时刻通过率是大于50的

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

//using namespace __gnu_cxx;
using namespace std;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

int yy[1010];

void init(){
	for (int i = 1; i <= 96; i ++)   yy[i] = i + 1;
}

inline int read(int out = 0)
{
    char c;
    while((c=getchar()) < 48 || c > 57);
    while(c >= 48 && c <= 57) out=out*10+c-48,c=getchar();
    return out; 
}

const int N = 1e5 + 10;
const int M = 2 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int cnt[50][3];
int n, m;
set<int> ss, ss2;

bool check(int id){
	int temp = cnt[id][0] + cnt[id][1];
	if (cnt[id][1] * 2 >= temp)    return true;
	else return false;
}

signed main(){
//	freopen("stdin.txt", "r", stdin);
//	freopen("stdout.txt", "w", stdout);
	cin >> m >> n;
	
	for (int i = 1; i <= m; i ++){
		int id;
		string str;
		cin >> id >> str;
		if (str == "AC")    cnt[id][1] ++;
		else   cnt[id][0] ++;
		
		ss2.insert(id);
		if (!check(id))   ss.insert(id);
	}
	
	bool flag = false;
	for (auto it = ss2.begin(); it != ss2.end(); it ++){
		if (!ss.count(*it)){
			cout << *it << " ";
			flag = true;
		}
	}
	
	if (!flag)   cout << "-1";
	cout << endl;
	
	return 0;
}

武辰延的字符串
链接:https://ac.nowcoder.com/acm/contest/9984/B
来源:牛客网

题目描述
众所周知,武辰延很喜欢字符串。

这天,他对着两个字符串 s 和 t 发呆,他发现这两个串的前缀有很多相似的地方,s 的两个前缀连接起来竟也是 t 的前缀。

武辰延想知道有多少对 s 的非空前缀连接起来是 t 的前缀。

形式化地讲,我们把 s_is
i

看作字符串 s 长度为 i 的前缀。

对于一对前缀 (s_i,s_j)(s
i

,s
j

) (允许 i=j)而言,当满足 s_i+s_j=t_{i+j}s
i

+s
j

=t
i+j

时,我们认为这两个 s 的前缀拼接后等于 t 的一个前缀。

两对 s 的前缀 (s_i,s_j)(s
i

,s
j

) 与 (s_{i’},s_{j’})(s
i


,s
j


) 不同当且仅当 i\neq i’i


=i

或 j\neq j’j


=j


输入描述:
第一行一个字符串 s 。

第二行一个字符串 t 。

其中 1 \le \mid s\mid, \mid t\mid \le 1e51≤∣s∣,∣t∣≤1e5 ,只包含小写字母。
输出描述:
输出一行一个整数,表示满足条件的前缀的对数。
示例1
输入
复制
aab
aaa
输出
复制
3
说明
s_1+s_1=t_2s
1

+s
1

=t
2

s_1+s_2=t_3s
1

+s
2

=t
3

s_2+s_1=t_3s
2

+s
1

=t
3
​ ,

我们首先考虑暴力的话如何做,我们首先得枚举第一个字符串的长度,然后枚举第二个字符串的长度,这两段的和和t字符串的前缀是否相等,看有多少满足条件的。我们首先找出有什么性质,第一个性质,第一个字符串的长度最大是s和t前缀相等的最大长度,我们可以从1到最大长度枚举,第二个性质是第二个字符串后面的最大匹配长度是多少,那么第一个字符串的种类就可以加上这个长度,而这个最大匹配长度是具有单调性的,我们可以二分求出

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 2e5 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

ULL h1[N], h2[N], p[N];
char s[N], t[N];

ULL get_hash1(int l, int r){
	return h1[r] - h1[l - 1] * p[r - l + 1];
}

ULL get_hash2(int l, int r){
	return h2[r] - h1[l - 1] * p[r - l + 1];
}

signed main(){
	ios;
	cin >> s + 1 >> t + 1;
	int n = strlen(s + 1), m = strlen(t + 1);
	
	p[0] = 1;
	for (int i = 1; i <= max(n, m); i ++){
		p[i] = p[i - 1] * PP;
	}
	
	for (int i = 1; i <= n; i ++){
		h1[i] = h1[i - 1] * PP + (s[i] - 'a' + 1);
	}
	
	for (int i = 1; i <= m; i ++){
		h2[i] = h2[i - 1] * PP + (t[i] - 'a' + 1); 
	}
	
	int ans = 0;
	for (int i = 1; i <= n; i ++){
		if (s[i] != t[i])   break;
		int l = 0, r = m - i;
		while(l < r){
			int mid = (l + r + 1) / 2;
			if (get_hash1(1, 1 + mid - 1) == get_hash2(i + 1, i + mid))   l = mid;
			else  r = mid - 1;
		}
		
		ans += l;
	}
	
	cout << ans << endl;
	
	return 0;
}

温澈滢的狗狗
链接:https://ac.nowcoder.com/acm/contest/9984/D
来源:牛客网

题目描述
众所周知,温澈滢在宿舍养了一排 n 只狗狗,每只狗狗都有一个颜色 c_ic
i

。同时,它们只喜欢和不同颜色的狗狗玩,否则它们会觉得很单调无趣。

也就是说,如果第 i 只狗狗和第 j 只狗狗颜色不同,那么它们可以拥有亲密关系,它们亲密度可以表示成 \mid i-j\mid∣i−j∣,否则它们就不能建立亲密关系。

当 n 只狗狗颜色两两不同时,它们有 \frac{n(n-1)}{2}
2
n(n−1)

对亲密关系;当 n 只狗狗的颜色完全一致时,它们之间就不存在任何一对亲密关系。

我们把这么多对亲密关系取出来,以亲密度为第一关键词,编号较小的狗狗的编号为第二关键词,编号较大的狗狗的编号为第三关键词排序(三维都按照升序排序)。

现在温澈滢想知道第 k 对亲密关系是哪一对狗狗,当然,必要的时候你可以告诉温澈滢这些狗狗之间不存在这么多的亲密关系。
输入描述:
第一行输入两个数字 n 和 k 。

第二行给出 n 个数字,第 i 个数字 c_ic
i

表示第 i 只狗狗的颜色。

其中 1\le n\le 1e51≤n≤1e5 ,1\le k\le \frac{n(n-1)}{2}1≤k≤
2
n(n−1)

,1\le c_i \le n1≤c
i

≤n
输出描述:
如果存在第 k 对狗狗,请在一行内输出两只狗狗的编号,小的在前,中间用一个空格隔开。

否则输出 -1 。
示例1
输入
复制
2 1
1 2
输出
复制
1 2
说明
只有 (1,2) 这一对亲密关系。
示例2
输入
复制
3 3
1 2 3
输出
复制
1 3
说明
三对亲密关系依次为 (1,2) (2,3) (1,3) 。
示例3
输入
复制
3 3
1 1 1
输出
复制
-1
说明
不存在任何一对亲密关系。
示例4
输入
复制
5 2
1 1 2 2 2
输出
复制
1 3

这个题目的特点是什么,和别的题目的区别是什么,是他的亲密度,这个亲密度是两个人的距离,而不是题目中输入的,而题目是求亲密的第k小的,所以我们如果能确定除第k小的亲密度是多大的话我们就可以很方便的通过枚举求出第k小的,我们如何求出这个亲密度,我们发现这个亲密度具有单调性,所以我们可以二分这个亲密度,又如何判断一个亲密度是否满足条件,我们可以求出小于等于这个亲密度的关系有多少对,如何求,我们可以先求出所有的关系有多少对,然后减去同一种颜色但是距离满足的,剩下的就是满足条件的亲密度个数,然后这样二分即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 2e5 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

vector<int> v[N];
int c[N];
int n, k;

int calc(int mid){
	int sum = mid * (n - mid) + mid * (mid - 1) / 2;
	for (int i = 1; i <= n; i ++){
		int k = 0;
		for (int j = 0; j < v[i].size(); j ++){
			while(v[i][k] - v[i][j] <= mid && k < v[i].size())   k ++;
			sum -= (k - j - 1);
		}
		
	}
	return sum;
}

signed main(){
	gt(n);
	gt(k);
	for (int i = 1; i <= n; i ++){
		gt(c[i]);
		v[c[i]].push_back(i);
	}
	
//	cout << "--" << endl;
	
	if (calc(n - 1) < k){
		cout << "-1" << endl;
        return 0;
	}
	
	int l = 0, r = n - 1;
	while(l < r){
		int mid = (l + r ) / 2;
		//cout << calc(mid) << endl;
		if (calc(mid) < k)    l = mid + 1;
		else   r = mid ;
	}
	
//	cout << l << endl;
	int temp = k - calc(l - 1);
	
	int cnt = 0;
	for (int i  = 1; i + l <= n; i ++){
		int ans = i + l;
		if (c[ans] != c[i])   cnt ++;
		if (cnt == temp){
			cout << i << " " << ans << endl;
			return 0;
		}
	}	
	
	return 0;
}

九峰与子序列
链接:https://ac.nowcoder.com/acm/contest/9984/E
来源:牛客网

学会字符串哈希后,动态规划选手九峰想要出一道解法为字符串哈希题,于是wcy给他口胡了一道题,却把九峰难倒了,你能帮他解决这个问题吗?
给定长度为n的字符串序列a和字符串k,询问a有多少子序列拼接起来等于k。
输入描述:
第一行输入一个正整数n(n\leq 40)(n≤40)和字符串k(|k|\leq 510^6)(∣k∣≤5∗10
6
)
第二行输入n个字符串a_1,a_2,…,a_na
1

,a
2

,…,a
n

,表示给定序列
数据保证a中的字符串总长度不超过5
10^65∗10
6
,输入的所有字符均为小写字母
输出描述:
一行输出一个整数,表示答案
示例1
输入
复制
5 abcba
ab c abc ba abcba
输出
复制
3
说明
拼接后等于abcba的子序列有三种:[1,2,4],[3,4],[5]

我们可以考虑暴力搜索,但是数据范围还是有点大,所以可以从两个方向分别搜索,我们可以搜索出前20个子序列可以组成k串的前多少个字母母的方案数,可以搜索出后20个序列可以组成k串的后多少个字母的方案数,然后无缝斜街乘积起来就可以了,然后加上只有前半部分组成的,加上只有后半部分组成的即可。如何搜索,我们可以看当前的每个序列能否和当前匹配的地方匹配,如果能的话,就可以考虑下一层,然后没走到下一层,就在上一层的前一个字母的位置的方案数加一就可以了,然后在搜索的过程中可以用哈希来优化

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 5e6 + 100;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

ULL h[N], a[N], p[N];
int n, m;
char s[N], t[N];
int len[50];
int cnt1[N], cnt2[N];

ULL calc(int lens){
	ULL res = 0;
	for (int i = 1; i <= lens; i ++){
		res = res * PP + (t[i] - 'a' + 1);
	}
	return res;
}

ULL get_hash(int l, int r){
	return h[r] - h[l - 1] * p[r - l + 1];
}

void dfs1(int x, int l){
	cnt1[l - 1] ++;
	if (x > m)    return;
	
	for (int dp = x; dp <= m; dp ++){
		if (a[dp] == get_hash(l, l + len[dp] - 1))     dfs1(dp + 1, l + len[dp]);
	}
}

void dfs2(int x, int r){
	cnt2[r + 1] ++;
	if (x <= m)   return;
	
	for (int dp = x; dp > m; dp --){
		if (a[dp] == get_hash(r - len[dp] + 1, r))   dfs2(dp - 1, r - len[dp]);
	}
}

signed main(){
	ios;
	cin >> n >> s + 1;
	
	p[0] = 1;
//	cout << strlen(s + 1) << endl;
	for (int i = 1; i <= strlen(s + 1); i ++){
		h[i] = h[i - 1] * PP + (s[i] - 'a' + 1);
		p[i] = p[i - 1] * PP;
	}
	
	for (int i = 1; i <= n; i ++){
		cin >> t + 1;
	//	cout << strlen(t + 1) << endl;
		a[i] = calc(strlen(t + 1));
		len[i] = strlen(t + 1);
	}	
	
	m = n / 2;
	
	dfs1(1, 1);
	dfs2(n, strlen(s + 1));
	
	int lengths = strlen(s + 1);
	
	int ans = 0;
	for (int i = 1; i < lengths; i ++){
		ans += (cnt1[i] * cnt2[i + 1]);
	}
	
	ans = ans + cnt1[lengths] + cnt2[1];
	
	cout << ans << endl;
	
	return 0;
}

魏迟燕的自走棋
链接:https://ac.nowcoder.com/acm/contest/9984/F
来源:牛客网

众所周知,魏迟燕正带领着他的自走棋小队驰骋疆场。

自走棋这个游戏不需要过硬的操作能力,只需要搭配阵容并分配装备即可,非常适合魏迟燕这种手残党。

现在魏迟燕的小队中有 n 个人,仓库中有 m 件装备,每个人只能装备一件装备,每件装备只能分配给一个人。

其中,第 i 件装备可以给 k_ik
i

个人中的一个,分别为 p_1,\cdots,p_{k_i}p
1

,⋯,p
k
i


,获得的战力提升为 w_iw
i

,总战力提升即为所有士兵战力提升之和。

魏迟燕想知道他能获得的最大总战力提升为多少?
输入描述:
第一行两个整数 n 和 m ,分别表示人数和装备数。

接下来 m 行,其中的第 i 行描述第 i 件装备的信息。

每行第一个整数 k_ik
i

表示这件装备适用的人数,之后 k_ik
i

个整数表示每个适用者的编号,最后一个整数 w_iw
i

表示装备这件装备带来的战力提升。

其中 1\le n,m\le 1e51≤n,m≤1e5,1\le k_i\le 21≤k
i

≤2,1\le p_i\le n1≤p
i

≤n,1\le w_i\le1e91≤w
i

≤1e9 。
输出描述:
一行输出一个整数表示能获得的最大战力提升。
示例1
输入
复制
3 2
2 2 1 5
1 1 3
输出
复制
8
说明
第一件装备给第二个人,第二件装备给第一个人。

再发现这道题的特殊的地方,k的范围不是0就是1,敏感的话就可以想到这道题可以等效为图的问题,然后再考虑,我们如果把装备的攻击力从大到小排序的话,大的如果可以用的话就一定要用它,如果k为1的话,就看这个人有没有武器,如果k为2的话,就看这两个人有没有一个人是没有武器的,如何维护这些东西,当我们放到图里的时候,就很清晰了,两个人用一个装备,如果这两个人的集合以前都没有用过武器的话,就还可以用一件武器,如果有一个人用过的话,以后这两个人就不能用武器了,但是这件武器是还可以用的,如果这两个人分别在两个集合的话道理也是一样的,而且,任何一个集合都只能再用一个武器,所以我们用并查集就可以了,并判断这个集合是否还可以用武器

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e5 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int p[N];
int flag[N];
int n, m;

struct Node{
	int a, b;
	int w;
	bool operator<(const Node &W)const{
		return w > W.w;
	}
}node[N];

int find(int x){
	if (x != p[x])   p[x] = find(p[x]);
	return p[x];
}

signed main(){
	ios;
	cin >> n >> m;
	
	for (int i = 1; i <= n; i ++){
		p[i] = i;
		flag[i] = 0;
	}
	
	for (int i = 1; i <= m; i ++){
		int k;
		cin >> k;
		if (k == 1){
			int x;
			cin >> x;
			node[i].a = node[i].b = x;
			cin >> node[i].w;
		}
		else {
			cin >> node[i].a >> node[i].b >> node[i].w;
		}
	}
	
	sort(node + 1, node + 1 + m);
	
	int ans = 0;
	for (int i = 1; i <= m; i ++){
		int w = node[i].w;
		int a = find(node[i].a), b = find(node[i].b);
		
		if (a != b && (flag[a]  == 0 || flag[b] == 0)){
			p[a] = b;
			ans += w;
			flag[b] = (flag[a] || flag[b]);
		}
		else{
			if (flag[a] == 0){
				ans += w;
				flag[a] = 1;
			}
		}
	}
	
	cout << ans << endl;
	
	return 0;
}

九峰与蛇形填数
链接:https://ac.nowcoder.com/acm/contest/9984/G
来源:牛客网

蛇形填数是一道经典的入门题,但是九峰有自己的想法,他认为盘着的蛇不是一条好蛇,只有不断前进才能突破自我,变成真龙,因此,相比于将矩阵填数成回型(盘踞的蛇):

\begin{matrix} 1& 2 &3 \ 8 & 9 &4 \ 7 & 6 & 5 \end{matrix}
1
8
7

2
9
6

3
4
5

九峰更喜欢将矩阵填成"S"型(行走的蛇):

\begin{matrix} 1 & 2 & 3 \ 6 & 5 & 4 \ 7 & 8 & 9 \end{matrix}
1
6
7

2
5
8

3
4
9

现在给你一个n*n的初始全零的矩阵,请你将其按第二种方法填数,但是这样子太过简单,所以每一次操作九峰会选择一个子矩阵,请你在其子矩阵上进行填数,并在最后输出整个矩阵
输入描述:
第一行两个整数n,m,表示矩阵的大小和操作次数(n\leq 2000,m\leq 3000)(n≤2000,m≤3000)
接下来m行,每行输入三个正整数x,y,k,表示在以(x,y)为左上角,边长为k的方阵内填数(1\leq x,y \leq n,max(x+k-1,y+k-1)\leq n)(1≤x,y≤n,max(x+k−1,y+k−1)≤n)
输出描述:
输出一个 n ∗ n n*n nn的矩阵,表示最后的结果
示例1
输入
复制
3 2
1 1 3
2 2 2
输出
复制
1 2 3
6 1 2
7 4 3

再发现有什么性质,然后我们再想办法优化,我们首先知道,如果一个点后面用了的话,那么前面在这个点的操作就没有意义,我们可以从后往前用每个操作,如果每个点后面用过的话,前面就不使用这个点了,然后的就是,知道一个操作的左上角和k的话,我们就可以知道这个操作的每个点的数字是多少,根据画图就可以出来,然后我们在前面枚举的时候,我们直接就可以知道这个操作在哪里就结束了,所以就知道在前面的操作中,这个点的下一个点是哪个,这样优化即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 3e3 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

struct Node{
	int x1, y1, x2, y2;
	int k;
}g[N];
int n, m;
int ne[N][N];
bool st[N][N];
int ans[N][N];

void work(int idx){
	int x1 = g[idx].x1, y1 = g[idx].y1, x2 = g[idx].x2, y2 = g[idx].y2;
	int k = g[idx].k;
	
	for (int i = x1; i <= x2; i ++){
		stack<int> sta;
		for (int j = y1; j <= y2; j = ne[i][j]){
			if (st[i][j])    continue;
			st[i][j] = true;
			
			sta.push(j);
			int num = (i - x1) * k;
			if (x1 % 2 == i % 2)    ans[i][j] = num + j - y1 + 1;
			else     ans[i][j] = num + k - (j - y1);
		}
		
		while(sta.size()){
			int t = sta.top();
			sta.pop();
			
			ne[i][t - 1] = ne[i][t];
		}
	}
}

signed main(){
	ios;
	cin >> n >> m;
	
	for (int i = 1; i <= n; i ++){
		for (int j = 1; j <= n; j ++){
			ne[i][j] = j + 1;
		}
	}
	
	for (int i = 1; i <= m; i ++){
		int x1, y1, k;
		cin >> x1 >> y1 >> k;
		g[i] = {x1, y1, x1 + k - 1, y1 + k - 1, k};
	}
	
	for (int i = m; i >= 1; i --)
		work(i);
		
	for (int i = 1; i <= n; i ++){
		for (int j = 1; j <= n; j ++){
			cout << ans[i][j] << " ";
		}
		cout << endl;
	}
	
	return 0;
}

#include<iostream>

using namespace std;
const int N=2010,M=3010;

int s[N][N],r[M][3],vis[N][N];
int n,m;

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++) scanf("%d%d%d",&r[i][0],&r[i][1],&r[i][2]);
    for(int i=m;i>=1;i--)
    {
        int a=r[i][0],b=r[i][1],c=r[i][2];
        for(int j=a;j<a+c;j++)
            for(int k=b;k<b+c;k++)
                if(!vis[j][k])
                {
                    if((j-a)&1) s[j][k]=(j-a)*c+c-(k-b);
                    else s[j][k]=(j-a)*c+k-b+1;
                    vis[j][k]=b+c-1;
                }
                else k=vis[j][k];
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)  printf("%d ",s[i][j]);
        puts("");
    }
    return 0;
}

吴楚月的表达式
链接:https://ac.nowcoder.com/acm/contest/9984/H
来源:牛客网

题目描述
众所周知,吴楚月在数据结构课的大作业环节选择了表达式求值。

他觉得实现一个线性的表达式求值太无聊了,于是他把问题丢到了一棵树上。

形式化地讲,这棵树有 n 个节点,1 号点为根,每个节点上都有一个权值 v_iv
i

,代表参与运算的值。每条边都有一个 op_iop
i

,代表运算符。

于是树上一条路径变成了 v-op-v-\cdots-v-op-vv−op−v−⋯−v−op−v 的形式,对应一个表达式。

吴楚月希望你对树上每一个节点 u ,计算出根到 u 的简单路径所对应的表达式的值。

由于计算结果可能很大,所以你需要对 1e9+7 取模 。

注:表达式优先级即正常的加减乘除的优先级,从左往右,乘除优先级高于加减。
输入描述:
第一行给出一个整数 n 表示节点个数。

第二行 n 个正整数,第 i 个数表示 i 号节点的权值 v_iv
i

第三行 n-1 个正整数,第 i 个数 fa_ifa
i

表示 i+1 号节点的父亲。

第四行一个长度为 n-1 的字符串, 第 i 个字符表示 i+1 号节点与它父亲之间连边对应的运算符。

其中 1\le n\le 1e51≤n≤1e5,1\le v_i \le 1e91≤v
i

≤1e9,1\le fa_i\le i1≤fa
i

≤i,op_i\in{+,-,,/}op
i

∈{+,−,∗,/}。
输出描述:
一行输出 n 个数字,其中第 i 个数字表示根到 i 号节点的路径所对应的表达式的值。
示例1
输入
复制
3
3 4 2
1 1
/

输出
复制
3 750000006 6

我们在树上进行操作,我们只要知道从起点到每个点的表达式,然后把每个表达式求出来就可以了,我们知道到每个点的表达式的话,只需要dfs就可以,于是我们就想在dfs的过程中把答案顺便求出来就更好了,在乘法的过程中我们应该如何表示,我们发现如果是加法的话,我们直接加就可以了,但是如果是乘法的话,我们需要先把上一个数拉出来,然后再乘,然后再加进去,所以我们需要维护上一个数,这样dfs就可以了

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 2e5 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int h[N], e[N], ne[N],idx;
int w[N];
char op[N];
int n;
int ans[N];

void add_edge(int a, int b){
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int qmi(int a, int b){
	int res = 1;
	while(b){
		if (b & 1)   res = res % mod * a % mod;
		a = a % mod * a % mod;
		b >>= 1;
	}
	return res % mod;
}

void add(int &sum, int val){
	sum = ((sum + val) % mod + mod) % mod;
}

void dfs(int u, int father, int sum, int val){
	if (u == 1)    sum = w[u], val = w[u];
	else{
		char c = op[u];
		if (c == '+'){
			add(sum, w[u]);
			val = w[u];
		}
		else if (c == '-'){
			add(sum, -w[u]);
			val = -w[u];
		}
		else if (c == '*'){
			add(sum, -val);
			val = val * w[u] % mod;
			add(sum, val);
		}
		else if (c == '/'){
			add(sum, -val);
		//	cout << val << "/" << w[u] << endl;
			val = val * qmi(w[u], mod - 2) % mod;
		//	cout << sum << "+" << val << endl;
			add(sum, val);
		}
	}
	
	ans[u] = sum;
	
	for (int i = h[u]; ~i; i = ne[i]){
		int j = e[i];
		if (j == father)   continue;
		dfs(j, u, sum, val);
	}
}

signed main(){
	ios;
	cin >> n;
	for (int i = 1; i <= n; i ++){
		cin >> w[i];
	}
	
	memset(h, -1, sizeof h);
	for (int i = 2; i <= n; i ++){
		int fa;
		cin >> fa;
		add_edge(fa, i);
	}
	
	for (int i = 2; i <= n; i ++){
		cin >> op[i];
	}
	
	dfs(1, -1, 0, 0);
	
	for (int i = 1; i <= n; i ++){
		cout << ans[i] << " ";
	}
	
	cout << endl;
	
	return 0;
}

九峰与分割序列
链接:https://ac.nowcoder.com/acm/contest/9984/I
来源:牛客网

给出一个序列,将其分割成若干个子区间,子区间的贡献为:若前一个子区间的长度大于k且该区间长度小
于等于k,则贡献为区间和的两倍,否则贡献为区间和,求一种分割方法,使得所有子区间贡献之和最大,输出最大贡献。
输入描述:
第一行输入两个正整数n和k(k\leq n\leq 10^5)(k≤n≤10
5
)

第二行输入n个正整数a_1,a_2,…,a_na
1

,a
2

,…,a
n

(对于所有1\leq i \leq n,a_i\leq 10^9)(对于所有1≤i≤n,a
i

≤10
9
)
输出描述:
输出一个整数,表示最大贡献和
示例1
输入
复制
5 2
1 2 3 4 5
输出
复制
24
说明
将序列分割为[1,2,3] [4,5] 则答案为(1+2+3)+(4+5)*2=24
备注:
第一个子区间的的前一个子区间长度视为0

考虑动态规划,f[i][0】表示前i个区间已经分割好,并且最后一个序列的最后一个字母是第i个字符,并且最后一个区间的长度是小于等于k的方案数,1就是大于k的方案数,然后状态转移即可,转移方程在代码里面,状态转移方程需要注意的有两点,一个是从k个字符以内转移,这样的话我们可以搞一个队列来优化,维护队列的单调递减,每次使用队头,另外一个是在很远的转移,这样我们一直维护这个值的最大值就可以了,然后在转移的时候使用

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e5 + 10;
const int M = 3 * N;
const int mod = 998244353;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int a[N];
int sum[N];
int f[N][2];
/* 
f[i][0/1]表示前i个数已经分好了,并且最后一段是小于等于k的(0),还是大于的(1)
f[i][0] = f[j][1] - 2 * sum[j] + 2 * sum[i];
f[i][0] = f[j][0] - sum[j] + sum[i];
f[i][1] = f[j][0] - sum[j] + sum[i];
f[i][1] = f[j][1] - sum[j] + sum[i];
*/
int n, k;

deque<int> q1, q2;

int get_val1(int x){
	return f[x][1] - 2 * sum[x];
}

int get_val2(int x){
	return f[x][0] - sum[x];
}

int get_val3(int x){
	return max(f[x][1], f[x][0]) - sum[x];
}

signed main(){
	ios;
	cin >> n >> k;
	for (int i = 1; i <= n; i ++)      cin >> a[i];
	
	for (int i = 1; i <= n; i ++){
		sum[i] = sum[i - 1] + a[i];
	}
	
	f[0][0] = 0;
	
	int maxd = 0;
	for (int i = 1; i <= n; i ++){
		while(q1.size() && q1.front() + k < i)    q1.pop_front();
		if (f[i - 1][1]){
			while(q1.size() && get_val1(i - 1) >= get_val1(q1.back()))	  q1.pop_back();
			q1.push_back(i - 1);
		}
		if (q1.size())    f[i][0] = max(f[i][0], get_val1(q1.front()) + 2 * sum[i]);
		
		while(q2.size() && q2.front() + k < i)   q2.pop_front();
		while(q2.size() && get_val2(i - 1) >= get_val2(q2.back()))       q2.pop_back();
		q2.push_back(i - 1);
		
		if (q2.size())   f[i][0] = max(f[i][0], get_val2(q2.front()) + sum[i]);
		
		if (i > k){
			maxd = max(maxd, get_val3(i - k - 1));
			f[i][1] = max(f[i][1], maxd + sum[i]);
		}
	}
	
	cout << max(f[n][0], f[n][1]) << endl;
	
	return 0;
}

邬澄瑶的公约数
链接:https://ac.nowcoder.com/acm/contest/9984/J
来源:牛客网

题目描述
众所周知,邬澄瑶正在学习欧几里得算法。

现在她已经可以轻松求解 \gcd(x_1,\cdots,x_n)gcd(x
1

,⋯,x
n

),并为此洋洋得意。为了整治狂妄自大的邬澄瑶,她的室友把 \gcd(x_1{p_1},\cdots,x_n{p_n})gcd(x
1
p
1


,⋯,x
n
p
n


) 这个式子甩给了他。

邬澄瑶被难住了,只好来求助于你,希望你帮她求出这个式子。

由于结果可能很大,你需要对 1e9+7 取模。

特别地,邬澄瑶的室友认为 \gcd(x) = xgcd(x)=x 。
输入描述:
第一行一个数表示 n 。

第二行 n 个数,第 i 个数表示 x_ix
i

第三行 n 个数,第 i 个数表示 p_ip
i

其中,1\le n,x_i,p_i\le1e41≤n,x
i

,p
i

≤1e4 。
输出描述:
输出一行一个数表示答案。
示例1
输入
复制
2
9 3
1 2
输出
复制
9
说明
\gcd(91,32)=9gcd(9
1
,3
2
)=9

我们可以知道,一些数的最大公约数一定是这些数都分解质因数,每个质因数的次数的最小值,而加上幂的话,就是每个次数乘对应的幂就是答案,统计每个质因数出现的最小次数即可

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <string>
#include <queue>
#include <map>
#include <stack>
#include <map>
#include <unordered_map>
#include <vector>
#include <cmath>
//#include <ext/rope>
#include <bits/stdc++.h> 

using namespace std;
//using namespace __gnu_cxx;

#define gt(x) x = read()
#define int long long
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl "\n"
//#define x first
//#define y second

int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0}; 

//typedef __int128 INT;
typedef pair<double, int> PDI;
typedef pair<int, int> PII;
typedef unsigned long long ULL;

inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 1e4 + 10;
const int M = 3 * N;
const int mod = 1e9 + 7;
const int PP = 131;
const int inf = 0x3f3f3f3f;
//const int INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-10;
const double PI = acos(-1);

int x[N], p[N];
int n;
int primes[N], cnt;
bool st[N];

void get_primes(){
	for (int i = 2; i <= N; i ++){
		if (!st[i])   primes[cnt ++] = i;
		for (int j = 0; primes[j] * i <= N; j ++){
			st[primes[j] * i] = true;
			if (i % primes[j] == 0)   break;
		}
	}
}
int nums[N];

int qmi(int a, int b){
	int res = 1;
	while(b){
		if (b & 1)   res = res * a % mod;
		a = a % mod * a % mod;
		b >>= 1;
	}
	return res % mod;
}

signed main(){
	ios;
	get_primes();
	cin >> n;
	
	for (int i = 1; i <= n; i ++){
		cin >> x[i];
	}
	
	for (int i = 1; i <= n; i ++){
		cin >> p[i];
	}
	
	memset(nums, 0x3f, sizeof nums);
	for (int i = 1; i <= n; i ++){
		for (int j = 0; j < cnt; j ++){
				int s = 0;
			
			if (x[i] % primes[j] == 0){			
				while(x[i] % primes[j] == 0 && x[i] / primes[j] >= 1){
					x[i] /= primes[j];
					s ++;
				}
			}
			
			nums[j] = min(nums[j], s * p[i]);
		}
	}
	
	int ans = 1;
	
	for (int i = 0; i < cnt; i ++){
		ans = ans % mod * qmi(primes[i], nums[i]) % mod;
		ans %= mod;
	}
	
	cout << ans % mod << endl;
	
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值