连续一段相同数字可以被视为一个数连续复制
for(int i=1;i<=n;++i)
{
cin>>a[i];
if(a[i] != a[i - 1]) cnt++;
}
cout<<cnt<<'\n';
二维DP,类似最长上升子序列,用dp[i][j]表示处理了1~i,第i位不变,删除,交换三种操作
dp[1][0] = 0, dp[1][1] = c1, dp[1][2] = c2;
for(int i=2;i<=n;++i)
{
//若保留第i项,枚举上一项
for(int j=0;j<i;++j)
{
if(a[j]<=a[i] && b[j]<=b[i]) dp[i][0] = min(dp[i][0], dp[j][0] + (i-j-1)*c1);
if(b[j]<=a[i] && a[j]<=b[i]) dp[i][0] = min(dp[i][0], dp[j][2] + (i-j-1)*c1);
}
//若删除第i项
dp[i][1] = min(dp[i][1],min({dp[i-1][0],dp[i-1][1],dp[i-1][2]}) + c1);
//若交换第i项,枚举上一项
for(int j=0;j<i;++j)
{
if(a[j]<=b[i] && b[j]<=a[i]) dp[i][2] = min(dp[i][2], dp[j][0] + (i-j-1)*c1 + c2);
if(b[j]<=b[i] && a[j]<=a[i]) dp[i][2] = min(dp[i][2], dp[j][2] + (i-j-1)*c1 + c2);
}
}
数学推导:设选择的子段是[l,r]则子段和是
,因为要求子段长度大于1,故除了l = 1 and r = 2的子段和一定可以写成一个大于1的奇数乘以偶数的形式,可以发现2的幂次不能满足。故写一个二分查找即可
auto check = [&](ll x)->ll
{
return x - __lg(2*x) + 1;
};
ll L = 0, R = 2*k;
while(L + 1 < R)
{
ll M = (L + R)/2;
if(check(M) < k) L = M;
else R = M;
}
cout<<2*R<<'\n';
Viva La Vida(Fried-Chicken's Version)
可以证明除了重边没有其它环存在,设由<a,b,c>三点构成一环,若<a,b>边是a选择的,<b,c>边是由b选择的,<c,a>边是由c选择的,可以发现矛盾即边<a,b> < 边<a,c>,边<b,c> < 边<b,a> ,边<c,a> < 边<c,b>。故对于每一条边单独考虑若是重边导致最终的连通块数目加一,该边同时是a点,b点所连边里最短的概率是
,共
条边
n = II()
C = n * (n - 1) % MOD * inv2 % MOD
INV = inv(2 * n - 3)
ans = C * INV % MOD
print(ans)
本题基于结论:随机分布时前缀最大值的变化次数期望为
,利用单调栈处理出每个数的下一个更大值的位置和更小值的位置,枚举左端点,将区间切分成至多
段分别统计不同最大最小值乘积的段数,最后将询问离线倒叙求前缀和即可
for(int i=1;i<=n;++i)
{
int maxn = a[i], minn = a[i];
int MAX_idx = nxt_max[i], MIN_idx = nxt_min[i];
int limit = min(MAX_idx, MIN_idx) - i;
cnt[(ll)maxn * minn] += limit;
int j = min(MAX_idx, MIN_idx);
maxn = max(maxn, a[j]), minn = min(minn, a[j]);
while(MAX_idx <= n || MIN_idx <= n)
{
if(MAX_idx == MIN_idx) MAX_idx = nxt_max[MAX_idx], MIN_idx = nxt_min[MIN_idx];
else if(MAX_idx > MIN_idx) MIN_idx = nxt_min[MIN_idx];
else MAX_idx = nxt_max[MAX_idx];
limit = min(MAX_idx, MIN_idx) - j;
cnt[(ll)maxn * minn] += limit;
j = min(MAX_idx, MIN_idx);
maxn = max(maxn, a[j]), minn = min(minn, a[j]);
}
}
先按照
由小到大排序,目标是取到最大的
,假设已经处理完前n - 1个offer,最大薪水为ans[n-1],若ans[n-1] >= a[n]则ans[n] = a[n] + b[n],否则ans[n] = max(a[n], a[n-1] + b[n-1])
for i in range(n):
a[i], b[i] = MI()
c.append((a[i], b[i]))
c.sort(key = lambda x: x[0] + x[1])
ans = 0
ans = c[0][0]
# 注意python的i-1可能访问到最后一个元素
for i in range(1,n):
if ans >= c[i][0]:
ans = c[i][0] + c[i][1]
else:
ans = max({c[i][0], ans, c[i-1][0] + c[i-1][1]})
print(ans)
设使用率表示为
可以发现x至多枚举到1000,一定会出现
落在[pi - 0.05, pi + 0.05)的区间内。故枚举x,看对于x是否所有的i都有对应的ci满足。将不等式变化形式使得用整数做所有运算可以发现check是O(1)的
auto check = [&](int x,int j)->bool
{
if(!p[j]) return true;
int L = (p[j] - 5) * x, R = (p[j] + 5) * x;
if((R - 1)/10000 - (L - 1)/10000 >= 1) return true;
return false;
};
for(int i=0;i<=1000;++i)
{
int j = 0;
while(j < n && check(i,j)) j ++;
if(j == n) {ans2 = i;break;}
}
对于胜率也是类似的道理,但胜率需要保证有赢的场次也有输的场次。
故ans = +
int max_w = 0, max_l = 0;
//因为[L,R)至多有10000个数不可能有超过一个的10000倍数
auto update = [&](int x,int j)->bool
{
int L = (p[j] - 5) * x, R = (p[j] + 5) * x;
if((R - 1)/10000 - (L - 1)/10000 >= 1)
{
int c = (R - 1) / 10000;
max_w = max(max_w, c);
max_l = max(max_l, x - c);
return true;
}
return false;
};
//关于有多个x取到相同胜率区间a/b ~~ c/d
//a < c,b < d 则选c/d的max_w和max_l一定不会优于选a/b
for(int i=0;i<n;++i)
{
if(!p[i]) continue;
for(int j = 1;j<=1000;++j)
{
if(update(j, i)) break;
}
}
ans1 = max_w + max_l;
易发现若查询区间的长度为偶数,直接反序即可,若为奇数,可以先构造限制最强的即任意长度为三的连续子区间,观察排列可以发现构造规律
if flag :
for j in range(0, n - 1, 2):
a[j], a[j + 1] = a[j + 1], a[j]
# print(a)
for i in range(n):
print(a[i], end = " " if i != n - 1 else "\n")
离线查询使用树状数组即可
for(int i=1;i<=n;++i)
{
//当op是1是先查询[0,val-1]后插入
//当op是2时先插入查询a[i]再查询
bool flag = false;
for(auto [op,val,idx] : query[i])
{
if(op == 1)
{
ans[idx] = A.sum(val - 1);
//cout<<"L "<<val<<' '<<idx<<' '<<ans[idx]<<'\n';
}
else
{
if(!flag) {A.update(a[i], 1);flag = true;}
int tmp = A.sum(val - 1);
ans[idx] = tmp - ans[idx];
//cout<<"R "<<val<<' '<<idx<<' '<<ans[idx]<<'\n';
}
}
if(!flag) A.update(a[i], 1);
}
枚举开始砍的日期即可
ll ans = 0;
for(int i=1;i<=y;++i)
{
//可以保持y-i+1天的x+i战斗力
if(y >= n) ans = max(ans, (ll)(n - i + 1) * (x + i));
else
{
ll tmp = (ll)(y - i + 1)*(x + i);
if(n - y >= x + i - 1) tmp += (x + i) * (x + i - 1) / 2;
else tmp += (x + i - 1 + x + i - (n - y)) * (n - y) / 2;
ans = max(ans, tmp);
}
//cout<<i<<' '<<ans<<'\n';
}
cout<<ans<<'\n';