DIFERENCIJA
求一个数组所有连续子序列的最大值减最小值的权值总和
先转化为求所有连续子序列的最大值和所有连续子序列的最小值
采用
d
p
dp
dp加栈的想法,
d
p
1
[
i
]
dp1 [ i ]
dp1[i]代表以
i
i
i为右端点的前
i
i
i个区间的最大值
状态转移
d
p
1
[
i
]
=
d
p
1
[
p
o
s
]
+
(
i
−
p
o
s
)
∗
a
[
i
]
dp1[i] = dp1[pos] + (i - pos)*a[i]
dp1[i]=dp1[pos]+(i−pos)∗a[i]
p
o
s
pos
pos代表前一个大于
a
[
i
]
a[i]
a[i]的位置 用一个模拟栈来实现找
p
o
s
pos
pos
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 100;
ll a[maxn], ans;
ll maxx, minn;
ll smaxx[maxn], sminn[maxn];
ll dp1[maxn], dp2[maxn], sum;
int main() {
int n, m;
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> a[i];
while (maxx && a[smaxx[maxx]] > a[i]) maxx--;
while (minn && a[sminn[minn]] < a[i]) minn--;
ll pos = sminn[minn];
dp1[i] = dp1[pos] + (i - pos)*a[i];
// cout << 1 << " " << i << " " << pos << ' ' << (i - pos)*a[i] << ' ' << ans1[pos] << " " << ans1[i] << endl;
pos = smaxx[maxx];
dp2[i] = dp2[pos] + (i - pos)*a[i];
//cout << 2 << " " << i << " " << pos << ' ' << (i - pos)*a[i] << ' ' << ans2[pos] << " " << ans2[i] << endl;
sum += dp1[i] - dp2[i];
smaxx[++maxx] = i;
sminn[++minn] = i;
}
cout << sum << endl;
return 0;
}
HONI
这道题是场上队友秒掉的%%tql
第
a
[
i
]
a[i]
a[i]个数代表困难度为
i
i
i的题目的数量 第
b
[
i
]
b[i]
b[i]代表困难度为
i
i
i或者
i
+
1
i+1
i+1的题目数量
需要各种难度的题目各一道,要求会有几种方案数
用一个二维
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]代表第
i
i
i种难度不选择
b
[
i
]
b[i]
b[i]的方案数 用
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]代表第
i
i
i种难度选择
b
[
i
]
b[i]
b[i]的方案数 最后答案贡献为
d
p
[
n
]
[
1
]
+
d
p
[
n
]
[
0
]
dp[n][1]+dp[n][0]
dp[n][1]+dp[n][0]
递推方程为
d
p
[
i
]
[
1
]
=
(
d
p
[
i
−
1
]
[
1
]
∗
(
a
[
i
]
+
b
[
i
−
1
]
)
+
d
p
[
i
−
1
]
[
0
]
∗
(
a
[
i
]
+
m
a
x
(
n
u
m
,
b
[
i
−
1
]
−
1
)
)
)
dp[i][1] = (dp[i - 1][1] * (a[i] + b[i - 1]) + dp[i - 1][0] * (a[i] + max(num, b[i-1] - 1)))%mod
dp[i][1]=(dp[i−1][1]∗(a[i]+b[i−1])+dp[i−1][0]∗(a[i]+max(num,b[i−1]−1))) 和
d
p
[
i
]
[
0
]
=
b
[
i
]
∗
(
d
p
[
i
−
1
]
[
0
]
+
d
p
[
i
−
1
]
[
1
]
)
dp[i][0] = b[i] * (dp[i - 1][0] + dp[i - 1][1]) % mod
dp[i][0]=b[i]∗(dp[i−1][0]+dp[i−1][1])
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 100;
ll a[maxn], ans;
ll b[maxn];
ll dp[maxn][3];
ll num;
const int mod = 1e9 + 7;
int main() {
ll n, m;
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
for (int i = 1; i < n; i++) cin >> b[i];
dp[1][1] = a[1]; dp[1][0] = b[1];
for (int i = 2; i <= n; i++) {
dp[i][1] = (dp[i - 1][1] * (a[i] + b[i - 1]) + dp[i - 1][0] * (a[i] + max(num, b[i-1] - 1)))%mod;
dp[i][0] = b[i] * (dp[i - 1][0] + dp[i - 1][1]) % mod;
}
cout << (dp[n][1] + dp[n][0]) % mod << endl;
return 0;
}
DVONIZ
求前
k
k
k元素和后
k
k
k个元素的和都不大于
S
S
S时,则认为这个序列是有趣的,找出从每个
i
i
i开始的最长的有趣的子序列
开始想用二分写 仔细一想这里的区间增加不是完全单调的,但是如果枚举中间点的话同时向两边扩张,这样的区间一定是单调的,找出每个
i
i
i向两边扩张的最大值,记下来,最后输出的时候再取一个max就好了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100;
const int mod = 1e9 + 7;
ll l[maxn], r[maxn];
ll a[maxn], sum[maxn];
ll ans[maxn];
int main() {
ios::sync_with_stdio; cin.tie(0); cout.tie(0);
ll n, s;
cin >> n >> s;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
for (int i = 1; i <= n; i++) {
l[i] = l[i - 1];
r[i] = r[i - 1];
while (l[i] <= n && sum[i - 1] - sum[l[i] - 1]>s)l[i]++;
while (r[i] + 1 <= n && sum[r[i] + 1] - sum[i - 1] <= s)r[i]++;
ll anss = min(i - l[i], r[i] - i + 1);
ans[i - anss] = max(anss, ans[i - anss]);
}
for (int i = 1; i <= n; i++) {
ans[i] = max(ans[i], ans[i - 1] - 1);
cout << ans[i] * 2 << endl;
}
return 0;
}
POŠTAR
找到一条最大值减最小值最小的路,一看范围 n n n才50,那就直接暴搜了,然后加一点优化,固定最小值找满足条件的最小的最大值,那就敲一个二分就好了,比赛敲的时候忘记把开始点的权值也打进去(wa了一发哭哭)
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn = 60;
const int N = 6000;
int sx, sy;
int cnt;
char mp[maxn][maxn];
int vis[maxn][maxn];
int b[N];
int dx[8] = { 0,0,1,1,1,-1,-1,-1 };
int dy[8] = { 1,-1,1,0,-1,1,0,-1 };
int a[maxn][maxn];
int ans = 0;
void dfs(int x, int y, int l, int r) {
for (int i = 0; i <= 7; i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if (xx >= 1 && xx <= n && yy >= 1 && yy <= n && !vis[xx][yy] && a[xx][yy] >= l && a[xx][yy] <= r) {
if (mp[xx][yy] == 'K') ans++;
vis[xx][yy] = 1;
dfs(xx, yy, l, r);
}
}
}
bool check(int l, int r) {
ans = 0;
memset(vis, 0, sizeof vis);
vis[sx][sy] = 1;
dfs(sx, sy, l, r);
if ( a[sx][sy]<l || a[sx][sy]>r ) return 0;
//cout <<l<<" "<<r<<" "<<cnt<<" "<< ans << endl;
return ans == cnt;
}
int main() {
ios::sync_with_stdio; cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> mp[i][j];
if (mp[i][j] == 'P') {
sx = i; sy = j;
}
if (mp[i][j] == 'K') cnt++;
}
}
int maxx = 0; int minn = 1e9;
int cnt1 = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
if (mp[i][j] == 'P' || mp[i][j] == 'K') {
maxx = max(maxx, a[i][j]);
minn = min(minn, a[i][j]);
}
b[++cnt1] = a[i][j];
}
}
sort(b + 1, b + cnt1 + 1);
int anss = 1e9;
for (int i = 1; i <= cnt1; i++) {
int l = b[i]; int r = b[cnt1]+1;
// cout << l << " " << r << endl;
while (l < r) {
int mid = (l + r) >> 1;
// cout << mid << endl;
if (check(b[i], mid)) {
r = mid;
anss = min(anss, r - b[i]);
}
else {
l = mid + 1;
}
}
if (b[i] == minn) break;
}
cout << anss << endl;
return 0;
}
/*
2
P.
.K
81 1
1 1
*/
KUGLICE
有一个有向图,其中每个点的出度为0或者1,有以下询问:
1
x
x
x:给定弹珠的起始点,求弹珠运动的终止点
2
x
x
x 删除
x
x
x点的出边
反向思考先构建出最后删完边的图 然后存起来记下来 再一点点加边 输出就好了
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 100;
bool vis[maxn];
int fa[maxn], root[maxn],son[maxn];
int find(int x) { return root[x] == x ? x : root[x] = find(root[x]); }
int n;
struct node {
int op, x;
}a[maxn];
int ans[maxn];
int main() {
cin >> n;
for (int i = 1; i <= n+1; i++) root[i] = i;
for (int i = 1; i <= n; i++) {
int x; cin >> x;
if (x == 0) {
vis[i] = 1;
}
son[i] = x;
}
int q; cin >> q;
for (int i = 1; i <= q; i++) {
int op, x;
cin >> op >> x;
if (op == 2) vis[x] = 1;
a[i].op = op; a[i].x = x;
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
int fx = find(i);
int fy = find(son[i]);
if (fx == fy) root[fx] = root[fy] = n + 1;
else root[fx] = fy;
}
}
int cnt = 0;
for (int i = q; i >= 1; i--) {
if (a[i].op == 2) {
int fx = find(a[i].x);
int fy = find(son[a[i].x]);
if (fx == fy) root[fx] = root[fy] = n + 1;
else root[fx] = fy;
}
else ans[++cnt] = find(a[i].x);
}
for (int i = cnt; i >= 1; i--) {
if (ans[i] == n + 1) cout << "CIKLUS" << endl;
else cout << ans[i] << endl;
}
return 0;
}