2022-05-19

http://oj.daimayuan.top/problem/199

给定一个长度为 nn 的数组 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,接下来进行 n−1 次操作。每次选择一个下标 x ,将 a x a_x ax a x + 1 a_{x+1} ax+1合并成 a x × a x + 1 m o d 1000003 a_x×a_{x+1}mod1000003 ax×ax+1mod1000003,并且你会获得 ( a x − a x + 1 ) 2 (a_x−a_{x+1})^2 (axax+1)2 的分数。

所以每次操作后,数组的长度将会减 1,当最后只剩下一个元素时停止操作。输出最终能获得的最大分数。

输入格式

第一行一个数字 n。

接下来一行 n 个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an

输出格式

一个数,表示答案。

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

所有数据保证 1 ≤ n ≤ 300 , 1 ≤ a i ≤ 1 0 6 1≤n≤300,1≤a_i≤10^6 1n300,1ai106

思路:

熟悉石子合并,应该能秒了这道题。

区间石子合并+乘法逆元

代码:
int n, m;
ll a[301], dp[301][301];

ll qmi(ll ba, ll p) {
	ll res=1;
	while(p){
		if(p&1)res=res*ba%mod;
		p>>=1;
		ba=ba*ba%mod; 
	}
	return res;
}

ll cal(int l, int r) { // 计算合并区间后的值
	ll res = a[r] * qmi(a[l-1], mod-2) % mod;
	return res;
}

void solve()
{
	cin >> n;
	
	a[0] = 1LL; 
	
	memset(dp, -0x3f, sizeof dp);
	
	for(int i = 0; i <= n; i++) {
		dp[i][i] = 0; // 区间长度为0
	}
	
	for(int i = 1; i <= n; i++) {
		cin >> a[i];
		if(i>1) a[i] = a[i-1] * a[i] % mod; 
	}
	
	for(int len = 2; len <= n; len++) {
		for(int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			for(int mid = l; mid < r; mid++) {
				ll L = cal(l,mid), R = cal(mid+1, r);
				ll val = (R-L)*(R-L);
				
				dp[l][r] = max(dp[l][r], dp[l][mid]+dp[mid+1][r] + val);
			}
		}
	}
	
	cout << dp[1][n] << endl;
}

http://oj.daimayuan.top/course/11/problem/80

有n个玩家参加比赛,他们分别有能力值 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an

需要进行n−1轮比赛,每一轮在剩下的玩家里任选两个玩家i,j。如果 ∣ a i − a j ∣ > K |a_i−a_j|>K aiaj>K,那么其中能力值高的玩家会获胜,能力值低的玩家会被淘汰。如果 ∣ a i − a j ∣ ≤ K |a_i−a_j|≤K aiajK,那么两个玩家都有可能获胜,另一个玩家被淘汰。

n − 1 n−1 n1轮比赛之后,只剩下一个玩家。问有多少个玩家可能是最后获胜的玩家。

输入格式

第一行,两个整数n,K,表示玩家的总人数,和获胜条件中的参数。

接下来一行n个整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,表示玩家的能力值。

输出格式

一个整数,表示最后可能获胜的玩家个数。

样例输入1
5 3
1 5 9 6 3
样例输出1
5
样例输入输出2

见下发文件。

数据规模

共10组数据。

测试点1满足n≤5。

测试点2满足n≤10。

测试点3,4,5满足n≤1000。

对于100%的数据,满足 n ≤ 1 0 5 , 1 ≤ a i , K ≤ 1 0 9 n≤10^5,1≤a_i,K≤10^9 n105,1ai,K109

思路:

小从到大排序,得到 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an

根据公式, a n a_n an 肯定会获胜,因为它必满足以上两个获胜条件。

∣ a i − a j ∣ > K |a_i−a_j|>K aiaj>K, a n a_n an 就是那个最大值

∣ a i − a j ∣ ≤ K |a_i−a_j|≤K aiajK, 两个值都有可能获胜

a n a_n an 从数组中剔除,接着考虑 a n − 1 a_{n-1} an1

∣ a i − a j ∣ > K |a_i−a_j|>K aiaj>K ,假设 a i > a j a_i>a_j ai>aj a j a_j aj再没有机会赢。因为它最后总会遇到比它大的数把它淘汰掉。

void solve()
{
	int ans = 1;
	cin >> n >> k;
	for(int i = 1; i <= n; i++) cin >> a[i];
	sort(a+1,a+1+n);
	for(int i = n; i >= 2; i--) {
		if(a[i] - a[i-1] <= k) ans++;
		else break;
	}
	cout << ans << endl;
}

http://oj.daimayuan.top/problem/605

便利蜂的货架上摆了一排蒟蒻果冻,搞得鶸尛鱻眼花缭乱…

对于每个果冻,都有一个价格 w 和口感 t。鶸尛鱻有一个购物篮子,在挑选蒟蒻果冻的时候,他有以下几种操作:

  • 操作 1:把一个价格为 w,口感为 t 的果冻放入篮子。
  • 操作 2:拿出篮子中 最为廉价 的果冻。
  • 操作 3:拿出篮子中 口感最差 的果冻。(t 越小,口感越差)

鶸尛鱻不喜欢重复,当操作 1 的 价格或口感 与篮中已有果冻重复时,他会立刻将其放回货架。

经过 n 次操作后,鶸尛鱻确定了要购买的若干果冻,请你帮他求出篮子里果冻的总价格。

输入格式

第 1 行一个正整数 n,代表操作次数。

第 2 行至第 (n+1) 行,每行 一个或三个 整数,分别表示 op,w,t。

w 和 t 当且仅当 op=1 时存在。

输出格式

输出一个整数,表示篮子里果冻的总价格。

样例输入
6
1 1 1
1 2 5
2
1 3 3
3
1 5 2
样例输出
7
数据规模

所有数据保证 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105 1 ≤ w , t ≤ 1 0 6 1≤w,t≤10^6 1w,t106,且保证输入合法

思路

找出最小值:用set存

关系:map

代码
int n, op, w, t;
void solve()
{
	cin >> n;
	set<int> s1, s2;
	map<int,int> mp1, mp2;
	while(n--) {
		cin >> op;
		if(op==1) {
			cin >> w >> t;
			if(mp1[w] != 0) continue; // 如果存在相同价格的物品。因为两个值都大于0,所有把等于0置为不存在
			if(mp2[t] != 0) continue; // 如果存在相同重量的物品
			s1.insert(w); // 自动排序,方便取最小值
			s2.insert(t); 
			mp1[w] = t; // 存两个映射关系
			mp2[t] = w;
		}
		else if(op==2) {
			int mi = *s1.begin(); // 当前最廉价
			s1.erase(mi); // 剔除该物品的价格
			int tmp = mp1[mi]; // 最廉价物品对应的质量
			s2.erase(tmp); // 剔除改物品的质量
			mp1[mi] = 0; // 消除映射关系
			mp2[tmp] = 0;
		}
		else {
			int mi = *s2.begin();
			s2.erase(mi);
			int tmp = mp2[mi];
			s1.erase(tmp);
			mp2[mi] = 0;
			mp1[tmp] = 0;
		}
	}
	ll ans = 0;
	for(auto x : mp1) {
		if(x.ss!=0) ans += x.ff; // 不为0,即是存在该物品
		
	}
	cout << ans << endl;
}

http://oj.daimayuan.top/course/11/problem/563

对于一个字符串 S ,我们定义 f(S) 为 S 中出现的不同的字符个数。 例如$ f(aba)=2 , , f(abc)=3 , , ,f(aaa)=1$。

现在给定一个字符串 S (假设长度为 len),请你计算 ∑ i = 0 l e n − 1 ∑ j = i l e n − 1 f ( S [ i : j ] ) ∑_{i=0}^{len−1}∑_{j=i}^{len−1}f(S[i:j]) i=0len1j=ilen1f(S[i:j])

输入格式

输入一行包含一个由小写字母组成的字符串 S 。

输出格式

输出一个整数表示答案。

样例输入
ababc
样例输出
28
数据规模

所有数据保证字符串长度 l e n ≤ 1000000 len≤1000000 len1000000,字符串下标从 0 0 0 l e n − 1 len−1 len1

思路:

对于每个位置 i :从 i 到 n 每个位置(假设当前位置是p)上未出现过的字符对答案的贡献是 n - p(下标从0开始)

例如:

ababc

0: a 的贡献 5 - 0; b 的贡献是 5 - 1;c 的贡献是 5-4, 总共10

1: a 的贡献 5 - 2; b 的贡献是 5 - 1;c 的贡献是 5-4,总共8

2:a 的贡献 5 - 2; b 的贡献是 5 - 3;c 的贡献是 5-4,总共6

3:a 的贡献 0; b 的贡献是 5 - 3;c 的贡献是 5-4,总共3

3:a 的贡献 0; b 的贡献是 0;c 的贡献是 5-4,总共1

一共28

做法:把每个字母的下标存下来;枚举字符串下标,二分找出每个字母第一个大于当前下标的位置,求出贡献值。

int n;
vector<int> neg[30];
void solve()
{
	string s;
	cin >> s;
	n = s.size();
	for(int i = 0; i < n; i++) {
		int pos = s[i] - 'a';
		neg[pos].push_back(i);
	}
	
	ll ans = 0;
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < 26; j++) {
			if((int)neg[j].size()==0) continue;
			int p = lower_bound(neg[j].begin(), neg[j].end(),i)-neg[j].begin();
			if(p >= (int)neg[j].size()) continue;
			ans += n-neg[j][p];
		}
	}
	cout << ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值