ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

来自学长的馈赠3

2022-07-25 08:34:10  阅读:132  来源: 互联网

标签:来自 int mi mid 学长 -- while 馈赠 mx


A. 学数数

单调栈 + 排序 + 前缀和 + 二分

单调栈应该是左边乘右边,考场脑抽写成长度。

对\(lowe\_bound\),\(upper\_bound\)使用不熟

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

容易得出\(2 * S_n^m - C_n^m = S_{n + 1} ^ m\)

这样再结合定义,我们可以在杨辉三角上\(O(1)\)上下左右移动,然后用莫队处理即可。??

太奇妙了

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. 完美子图

两种解法

  1. 分治

  2. 线段树

首先读入\(x,y\)时令\(a[x] = y\)问题转化为多少区间值域连续,即\(max - min = r - l\)

先说分治

\(solve(l, r)\)处理当前区间

\(l - mid\),\(mid + 1 - r\)递归处理

考虑如何处理两侧合并的贡献

考虑两种情况,即最大值最小值在一侧/两侧

在一侧时根据\(max - min = r - l\)知三求一,判一下是否合法即可

在两侧时,以左小右大为例

移项得\(l - min = r - max\),

我们从\(mid - > l\)枚举\(i\),右区间进行双指针

实际上因为\(max_i,min_i\)的存在右区间因为\(max\)有一个下界,根据\(min\)有一个上界,而且这两个界一定是单调右移的,我们把符合要求的加进桶里,左边直接查桶即可

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;
}

线段树做法

从左往右第\(i\)个节点维护第\(i - r\)的答案

根据\(max- min + l >= r\)我们动态修改后查询最小值以及最小值个数即可统计答案

更新哪个区间?单调栈的同时进行更新即可

D. DP搬运工2

用之前的式子改了下转移,水到\(80\)

其实之前的式子中的\(j\)在这里可以砍掉

转移的话考虑当前数插在两边/两边的数原来有无贡献

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;
}

标签:来自,int,mi,mid,学长,--,while,馈赠,mx
来源: https://www.cnblogs.com/Chencgy/p/16516169.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有