来自学长的馈赠3
A. 学数数
单调栈 + 排序 + 前缀和 + 二分
单调栈应该是左边乘右边,考场脑抽写成长度。
对,使用不熟
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c; c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')x = (x << 3 ) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
typedef long long ll;
const int maxn = 100005;
int sta[maxn], l[maxn], r[maxn], top, a[maxn], p[maxn];
ll len[maxn], s[maxn];
bool cmp(int x, int y){return a[x] < a[y];}
char c[3];
signed main(){
int n = read(), Q = read();
for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= n; ++i){
while(top && a[sta[top]] < a[i])r[sta[top--]] = i - 1;
l[i] = sta[top] + 1; sta[++top] = i;
}
while(top)r[sta[top--]] = n;
for(int i = 1; i <= n; ++i)p[i] = i;
sort(p + 1, p + n + 1, cmp);
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; ++i)len[i] = (1ll * r[p[i]] - p[i] + 1) * (1ll * p[i] - l[p[i]] + 1);
for(int i = 1; i <= n; ++i)s[i] = s[i - 1] + len[i];
ll ans = 0;s[n + 1] = s[n];
for(int i = 1; i <= Q; ++i){
int x; scanf("%s%d",c, &x);
if(c[0] == '>')ans = s[n] - s[upper_bound(a + 1, a + n + 1, x) - a - 1];
if(c[0] == '<')ans = s[lower_bound(a + 1, a + n + 1, x) - a - 1];
if(c[0] == '=')ans = s[upper_bound(a + 1, a + n + 1, x) - a - 1] - s[lower_bound(a + 1, a + n + 1, x) - a - 1];
printf("%lld\n",ans);
}
return 0;
}
B. sum
容易得出
这样再结合定义,我们可以在杨辉三角上上下左右移动,然后用莫队处理即可。??
太奇妙了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c; c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')x = (x << 3 ) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
typedef long long ll;
const int maxn = 100000;
const int mod = 1e9 + 7;
const int len = 350;
int fac[maxn + 55], inv[maxn + 55];
inline int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
inline void pre(){
fac[0] = 1; for(register int i = 1; i <= maxn; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
inv[maxn] = qpow(fac[maxn], mod - 2); for(register int i = maxn - 1; i; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod; inv[0] = 1;
}
inline int C(int n, int m){return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
inline int S(int n, int m){ll ans = 0;for(register int i = 0; i <= m; ++i)ans += C(n, i);return ans % mod;}
int block[maxn + 55], nn, nm, ans[maxn + 55]; ll nans;
struct query{int n, m, id;}q[maxn + 55];
bool cmp(query x, query y){return block[x.n] == block[y.n] ? (block[x.n] & 1 ? x.m < y.m : x.m > y.m) : x.n < y.n;}
inline void add_n(){nans = (nans + nans - C(nn, nm) + mod) % mod;}
inline void red_n(){nans = (nans + C(nn - 1,nm)) * inv[2] % mod;}
inline void add_m(){nans = (nans + C(nn, nm + 1)) % mod;}
inline void red_m(){nans = (nans - C(nn, nm) + mod) % mod;}
inline void work(int Q){
ans[q[1].id] = S(q[1].n, q[1].m);
nn = q[1].n, nm = q[1].m, nans = ans[q[1].id];
for(register int i = 2; i <= Q; ++i){
for(; nn < q[i].n; ++nn)add_n();
for(; nm > q[i].m; --nm)red_m();
for(; nn > q[i].n; --nn)red_n();
for(; nm < q[i].m; ++nm)add_m();
ans[q[i].id] = nans;
}
}
int main(){
pre();
int id = read(), Q = read();
for(register int i = 1; i <= Q; ++i)q[i].n = read(), q[i].m = read();
for(register int i = 1; i <= Q; ++i)q[i].id = i;
for(register int i = 1; i <= maxn; ++i)block[i] = (i + len - 1) / len;
sort(q + 1, q + Q + 1, cmp);
work(Q);
for(register int i = 1; i <= Q; ++i)printf("%d\n",ans[i]);
return 0;
}
C. 完美子图
两种解法
-
分治
-
线段树
首先读入时令问题转化为多少区间值域连续,即
先说分治
处理当前区间
,递归处理
考虑如何处理两侧合并的贡献
考虑两种情况,即最大值最小值在一侧/两侧
在一侧时根据知三求一,判一下是否合法即可
在两侧时,以左小右大为例
移项得,
我们从枚举,右区间进行双指针
实际上因为的存在右区间因为有一个下界,根据有一个上界,而且这两个界一定是单调右移的,我们把符合要求的加进桶里,左边直接查桶即可
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c; c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')x = (x << 3 ) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
typedef long long ll;
const int maxn = 500005;
int n, a[maxn], mi[maxn], mx[maxn], cnt[maxn << 1 | 1];
ll ans = 0;
void solve(int l, int r){
if(l == r){++ans;return;}
int mid = (l + r) >> 1;
solve(l, mid); solve(mid + 1, r);
mx[mid] = mi[mid] = a[mid];
mx[mid + 1] = mi[mid + 1] = a[mid + 1];
for(int i = mid - 1; i >= l; --i){
mx[i] = max(mx[i + 1], a[i]);
mi[i] = min(mi[i + 1], a[i]);
}
for(int i = mid + 2; i <= r; ++i){
mx[i] = max(mx[i - 1], a[i]);
mi[i] = min(mi[i - 1], a[i]);
}
for(int i = mid; i >= l; --i){
int j = mx[i] - mi[i] + i;
if(j > mid && j <= r && mi[j] > mi[i] && mx[j] < mx[i]) ++ans;
}
for(int i = mid + 1; i <= r; ++i){
int j = i - mx[i] + mi[i];
if(j <= mid && j >= l && mi[j] > mi[i] && mx[j] < mx[i]) ++ans;
}
int j = mid + 1, k = mid + 1;
for(int i = mid; i >= l; --i){
while(j <= r && mi[j] > mi[i]){++cnt[mx[j] - j + n]; ++j;}
while(k < j && mx[k] < mx[i]){--cnt[mx[k] - k + n];++k;}
ans += cnt[mi[i] - i + n];
}
while(k < j){--cnt[mx[k] - k + n]; ++k;}
j = mid + 1, k = mid + 1;
for(int i = mid; i >= l; --i){
while(j <= r && mx[j] < mx[i]){++cnt[mi[j] + j]; ++j;}
while(k < j && mi[k] > mi[i]){--cnt[mi[k] + k]; ++k;}
ans += cnt[mx[i] + i];
}
while(k < j){--cnt[mi[k] + k]; ++k;}
// j = mid, k = mid;
// for(int i = mid + 1; i <= r; ++i){
// while(j >= l && mi[j] > mi[i]){++cnt[mx[j] + j]; --j;}
// while(k > j && mx[k] < mx[i]){--cnt[mx[k] + k];--k;}
// ans += cnt[mi[i] + i];
// }
// while(k > j){--cnt[mx[k] + k]; --k;}
}
int main(){
n = read();
for(int i = 1; i <= n; ++i){int x = read(), y = read();a[x] = y;}
solve(1, n);
printf("%lld\n",ans);
return 0;
}
线段树做法
从左往右第个节点维护第的答案
根据我们动态修改后查询最小值以及最小值个数即可统计答案
更新哪个区间?单调栈的同时进行更新即可
D. DP搬运工2
用之前的式子改了下转移,水到
其实之前的式子中的在这里可以砍掉
转移的话考虑当前数插在两边/两边的数原来有无贡献
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2051;
const int mod = 998244353;
int n, K;
ll f[maxn][maxn];
int main(){
scanf("%d%d", &n, &K);
f[1][0] = 1;
for(int i = 1; i < n; ++i){
for(int k = 0; k <= K; ++k)
if(f[i][k]){
f[i][k] %= mod;
f[i + 1][k] += 2 * (k + 1) * f[i][k];
f[i + 1][k + 1] += (i - 1 - k * 2) * f[i][k];
}
}
printf("%lld\n",f[n][K] % mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】