上海市计算机学会2022年11月乙组解题报告

上海市计算机学会2022年11月乙组解题报告

数对统计

题目描述

给定的 n n n 个数字 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,你可以从中任选两个数字,并按其在原先序列中顺序组成一个新的数对。(即:当所选数对为 ( a i , a j ) (a_i,a_j) (ai,aj)时, 满足 i < j i < j i<j)
请问按此方法,能组成多少种本质不同的数对?

输入格式

输入第一行,一个正整数
输入第二行, n n n个正整数 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an

输出格式

输出本质不同的数对个数

数据范围

∘ \circ 对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 10 1 \leq n \leq 10 1n10
∘ \circ 对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 1 0 3 1 \leq n \leq 10^3 1n103
∘ \circ 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105 1 ≤ a i ≤ n 1 \leq a_i \leq n 1ain

样例数据

输入:

4
3 5 3 2

输出:

5

说明:

可以组出(3,5),(3,3),(3,2),(5,3),(5,2)共5对本质不同的数对

思路

用排列组合公式直接获取答案,然后用map映射各数对的数目将答案进行处理, O ( n 2 ) O(n^2) O(n2)初评可得60分

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;

int n , ans;
string s[MAXN];
map<string , int> mp; 
map<string , int> mpp;
int main(){
	string st = "";
	cin >> n;
	ans = n * (n - 1) / 2;
	for(int i = 1; i <= n; ++i)
		cin >> s[i];
	for(int i = 1; i < n; ++i){
		mp[s[i]]++;
		if(mp[s[i]] != 1){
			ans -= (n - i);
			continue;
		}
		for(int j = i + 1; j <= n; ++j){
			st = s[i] + " " + s[j];
			if(mp[st] == 0)
				mp[st]++;
			else
				ans--;	
		}
	}
	cout << ans << endl;
	return 0;
}

思路二

∘ \circ 用sum记录数组中不同数字的个数,在数组中扫一遍;
∘ \circ 如果当前数字只有一个那么以这个数字为开始的数对就要减去一,若当前数字不止一个,则代表后面还有相同的数字而可以不用减去;
∘ \circ 同时用哈希表来标记以这个相同数字为数对第一个数的有没有被记录过,没记录过答案就加上

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;

long long n , sum , ans , a[MAXN] , b[MAXN];
bool vh[MAXN];

int main(){
	memset(vh , false , sizeof(vh));
	cin >> n;
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
		if(b[a[i]] == 0)
			sum++;
		b[a[i]]++;
	}
	for(int i = 1; i < n; ++i){
		b[a[i]]--;
		if(!b[a[i]]) sum--;
		if(!vh[a[i]]){ 
			ans += sum;
			vh[a[i]] = true;
		}
	}
	cout << ans << endl;
	return 0;
} 

思路三

将数依次扔到set里,对每个数统计一下前面set中元素个数
程序未实现

序列操作

题目描述

给定 n n n 个整数 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,一开始,所有数字都是 0 0 0,接下来将根据输入数据依次进行 q q q 条修改操作:

加法修改操作以字符 + 开头,后接两个整数 p p p d d d,表示数列的第 p p p 项将增加 d d d
乘法修改操作以字符 * 开头,后接一个整数 m m m,表示数列的每一项都将乘以 m m m
请输出经过修改后数列,由于答案可能很大,输出每一个数字模 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的余数。

输入格式

第一行:两个整数表示 n n n q q q
第二行到第 i + 1 i+1 i+1 行:第 i + 1 i + 1 i+1 行首先有一个字符表示操作类型,若是加法修改,后接两个整数 p i p_i pi d i d_i di ,若是乘法修改,后接一个整数 m i m_i mi

输出格式

单独一行: n n n 个数字表示修改后每个数字模 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的余数。

数据范围

∘ \circ 对于 40 % 40\% 40% 的数据, n , q ≤ 1000 n,q\leq 1000 n,q1000
∘ \circ 对于 80 % 80\% 80% 的数据, n , q ≤ 50000 n,q\leq 50000 n,q50000
∘ \circ 对于 100 % 100\% 100% 的数据, n , q ≤ 200 , 000 n,q\leq 200,000 n,q200,000
∘ \circ 1 ≤ d i , m i ≤ 100 , 000 1 \leq d_i, m_i \leq 100,000 1di,mi100,000

样例数据

输入:

3 5

  • 1 3
  • 10
  • 2 6
  • 3 9
  • 5

输出:

150 30 45

思路

直接暴力运算可得40分

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 10;
#define ll long long 
const ll MOD = 1e9 + 7;

ll n , q;
ll a[MAXN];

int main(){
	char ch;
	ll p , d , m;
	cin >> n >> q;
	for(int i = 1; i <= q; ++i){
		cin >> ch;
		if(ch == '+'){
			cin >> p >> d;
			a[p] += d;
			a[p] %= MOD;
		}
		else{
			cin >> m;
			for(int i = 1; i <= n; ++i)
				a[i] *= m , a[i] %= MOD;
		}
	}
	for(int i = 1; i < n; ++i)
		cout << a[i]  << " ";
	cout << a[n] << endl;
	return 0;
}

思路二

对于每个+ 和 * 进行维护 , 统计每次加上需要进行的*次数

代码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200010
#define ll long long
const int d=1e9+7;
int n,q,a[MAXN],b[MAXN];
char op[MAXN];
ll ans[MAXN],g=1;//当前已经被乘上多少了
int main(){
	cin>>n>>q;
	for(int i=1;i<=q;++i){
		cin>>op[i];
		if(op[i]=='+')
			cin>>a[i]>>b[i];
		else
			cin>>b[i];
	}for(int i=q;i>=1;--i){
		if(op[i]=='+')
			ans[a[i]]=(ans[a[i]]+b[i]*g%d)%d;//加法运算,去掉取模即为(ans[a[i]]+b[i]*g),ans为被运算的数组
		else
			g=g*b[i]%d;//乘法对g操作
	}for(int i=1;i<=n;++i)
		cout<<ans[i]<<" ";
	cout<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值