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 (ax−ax+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 1≤n≤300,1≤ai≤106
思路:
熟悉石子合并,应该能秒了这道题。
区间石子合并+乘法逆元
代码:
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 ∣ai−aj∣>K,那么其中能力值高的玩家会获胜,能力值低的玩家会被淘汰。如果 ∣ a i − a j ∣ ≤ K |a_i−a_j|≤K ∣ai−aj∣≤K,那么两个玩家都有可能获胜,另一个玩家被淘汰。
n − 1 n−1 n−1轮比赛之后,只剩下一个玩家。问有多少个玩家可能是最后获胜的玩家。
输入格式
第一行,两个整数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 n≤105,1≤ai,K≤109
思路:
小从到大排序,得到 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 ∣ai−aj∣>K, a n a_n an 就是那个最大值
当 ∣ a i − a j ∣ ≤ K |a_i−a_j|≤K ∣ai−aj∣≤K, 两个值都有可能获胜
把 a n a_n an 从数组中剔除,接着考虑 a n − 1 a_{n-1} an−1
当 ∣ a i − a j ∣ > K |a_i−a_j|>K ∣ai−aj∣>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 1≤n≤105, 1 ≤ w , t ≤ 1 0 6 1≤w,t≤10^6 1≤w,t≤106,且保证输入合法。
思路
找出最小值:用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=0len−1∑j=ilen−1f(S[i:j]) 。
输入格式
输入一行包含一个由小写字母组成的字符串 S 。
输出格式
输出一个整数表示答案。
样例输入
ababc
样例输出
28
数据规模
所有数据保证字符串长度 l e n ≤ 1000000 len≤1000000 len≤1000000,字符串下标从 0 0 0 到 l e n − 1 len−1 len−1。
思路:
对于每个位置 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;
}