CSP-S模拟7

A. 序列问题

考场先写出一个 \(f_i\) 表示长度为 \(i\) 的子序列的最大贡献, 然后发现每个点的贡献在坐标系里是一个三角形,非常恶心,然后不知道怎么想到换定义了

定义 \(f_i\) 为以 \(i\) 为结尾的,并且 \(i\) 位置有贡献的最大贡献

那么能转移到 \(i\)\(j\) 需要满足

\(j < i\)

\(a_j < a_i\)

\(j - a_j <= i - a_i\)

发现第一个条件可以由下面两个得到,那么只需要处理下面两个,那么这就是一个二维偏序 ,排序 + \(BIT\) 即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 500005;
int f[maxn], n, a[maxn], cnt;
struct node{
	int x, y;
	friend bool operator < (const node &x, const node &y){
		return x.y == y.y ? x.x < y.x : x.y < y.y;
	}
}d[maxn];
struct BIT{
	int t[maxn];
	int lowbit(int x){return x & -x;}
	void add(int x, int val){
		while(x <= n){
			t[x] = max(val, t[x]);
			x += lowbit(x);
		}
	}
	int query(int x){
		int ans = 0;
		while(x){
			ans = max(ans, t[x]);
			x -= lowbit(x);
		}
		return ans;
	}
}t;
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)if(a[i] <= i){d[++cnt].x = a[i]; d[cnt].y = i - a[i];}
	sort(d + 1, d + cnt + 1);
	int p = 1, ans = 0;
	while(p <= cnt){
		int nans = t.query(d[p].x - 1) + 1;
		ans = max(ans, nans);
		t.add(d[p].x, nans);
		++p;
	}
	printf("%d\n",ans);
	return 0;
}

B. 钱仓

首先显然可以断环为链, 然后考虑一个起点需要满足该位置有数, 上一个位置最大为 \(1\)

进一步发现如果一串连续的 \(1\) 那么从哪个开始本质相同, 于是起点变成上一个位置为 \(0\) 该位置有数

考虑推的过程中,如果当前起点推到某个位置推不下去,那么从该位置往后到那个位置都不满足条件,可以直接跳过

到此为止是我赛时做法,不知道能不能通过构造卡掉

其实离正解差一个结论,就是从不同合法起点开始得到的答案相同,因为各个起点其实独立处理了他到下一个起点的区间

那么我们只要找到答案就可以退了

正解还不太一样,他直接找了前缀和最大的点,这样每个位置的前缀和减出来都是合法的

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 100005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, c[maxn + maxn], tmp[maxn + maxn];
ll ans = inf;
int pos, flag;
ll solve(int l, int r){
	flag = 0;
	for(int i = l; i <= r; ++i)tmp[i] = c[i];
	int pr = l + 1;
	ll nans = 0;
	for(int pl = l; pl < r; ++pl){
		if(pl == pr) ++pr;
		if(tmp[pl] < 1){
			flag = 1;
			pos = pl;
			return inf;
		}
		while(tmp[pl] > 1){
			nans += (pr - pl) * (pr - pl);
			if(nans >= ans)return inf;
			--tmp[pl]; ++tmp[pr]; ++pr;
		}
	}
	return nans;
}
int main(){
	freopen("barn.in","r",stdin);
	freopen("barn.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)c[i] = read();
	for(int i = 1; i <= n; ++i)c[i + n] = c[i];
	ans = solve(1, n);
	for(int i = 2; i <= n; ++i)
		if(c[i] && c[i + n - 1] < 1){
			ans = min(ans, solve(i, i + n - 1));
			if(flag)i = pos;
			if(ans != inf)break;
		}
	printf("%lld\n",ans);
	return 0;
}

C. 自然数

线段树套路,但是考场想的是部分分给的启发,然后假了

考虑每次删去一个点的贡献

初始令 \(l = 1\) 线段树第 \(x\) 个叶子维护 \([l , x]\)\(mex\)

考虑每次移动 \(l - > l + 1\) 的影响, 他只会影响到下一个与 \(l\) 数字相同的位置之前

在该区间内,所有 \(mex\) 都变成原来的 \(mex\) 和删去的 \(l\) 位置的数的最小值

吉司机?

发现 \(mex\) 有单调性, 那么在线段树对应区间二分,然后区间覆盖即可

需要维护区间最小值,区间总和, 实现区间覆盖,区间查询

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 200005;
int n, a[maxn], me[maxn];
bool vis[maxn];
struct tree{
	struct node{
		int tag, mi;
		ll sum;
	}t[maxn << 2 | 1];
	void push_up(int x){
		t[x].sum = t[x << 1].sum + t[x << 1 | 1].sum;
		t[x].mi = min(t[x << 1].mi, t[x << 1 | 1].mi);
	}
	void built(int x, int l, int r){
		t[x].tag = -1;
		if(l == r){
			t[x].mi = t[x].sum = me[l];
			return;
		}
		int mid = (l + r) >> 1;
		built(x << 1, l, mid);
		built(x << 1 | 1, mid + 1, r);
		push_up(x);
	}
	void upd(int x, int l, int r, int val){
		t[x].mi = val;
		t[x].sum = 1ll * (r - l + 1) * val;
		t[x].tag = val;
	}
	void push_down(int x, int l, int r){
		int mid = (l + r) >> 1;
		upd(x << 1, l, mid, t[x].tag);
		upd(x << 1 | 1, mid + 1, r, t[x].tag);
		t[x].tag = -1;
		return;
	}
	void modify(int x, int l, int r, int L, int R, int val){
		int mid = (l + r) >> 1;
		if(L <= l && r <= R){
			if(t[x].mi > val){
				upd(x, l, r, val);
				return;
			}
			if(l == r)return;
			if(t[x].tag != -1)push_down(x, l, r);
			if(t[x << 1 | 1].mi > val){
				upd(x << 1 | 1, mid + 1, r, val);
				modify(x << 1, l, mid, L, R, val);
			}else{
				modify(x << 1 | 1, mid + 1, r, L, R, val);
			}
			push_up(x);
			return; 
		}
		if(t[x].tag != -1)push_down(x, l, r);
		if(L <= mid)modify(x << 1, l, mid, L, R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
	ll query(int x, int l, int r, int L, int R){
		if(L <= l && r <= R)return t[x].sum;
		if(t[x].tag != -1)push_down(x, l, r);
		int mid = (l + r) >> 1; ll ans = 0;
		if(L <= mid)ans +=  query(x << 1, l, mid, L, R);
		if(R > mid)ans += query(x << 1 | 1, mid + 1, r, L, R);
		return ans;
	}
}t;
int nxt[maxn], pos[maxn];
int main(){
	freopen("mex.in","r",stdin);
	freopen("mex.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	int mex = 0;
	for(int i = 1; i <= n; ++i){
		if(a[i] < n){
			vis[a[i]] = 1;
			while(vis[mex])++mex;
		}
		me[i] = mex;
	}
	t.built(1, 1, n);
	for(int i = n; i > 0; --i)if(a[i] < n){
		nxt[i] = pos[a[i]];
		pos[a[i]] = i;
	}
	ll ans = 0;
	for(int l = 1; l <= n; ++l){
		ans += t.query(1, 1, n, l, n);
		t.modify(1, 1, n, l, nxt[l] ? nxt[l] - 1 : n, a[l]);
	}
	printf("%lld\n",ans);
	return 0;
}

D. 环路

分治

发现其实把读入转成 \(0/1\) 矩阵 \(A\)

答案就是对 \(A, A^2, A^3 .... A^{k - 1}\) 的对角线求和

然后这个东西是可以分治优化的,简单来说就是类似快速幂的东西

\(\sum_{i = 1}^{k} A^i = \sum_{i = 1}^{k / 2}A^i + \sum_{i = 1}^{k / 2} A^{i}\times A^{k / 2} + [k \& 1] A^k\)

然后分治一下即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9'){
		if(c == 'Y')return 1;
		if(c == 'N')return 0;
		c = getchar();
	}
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 105;
int n, k, m;

struct matrix{
	int a[maxn][maxn];
	void clear(){for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j)a[i][j] = 0;}
	matrix(){memset(a, 0, sizeof(a));}
	friend matrix operator * (const matrix &x, const matrix &y){
		matrix c;
		for(int i = 0; i < n; ++i)
			for(int k = 0; k < n; ++k)
				for(int j = 0; j < n; ++j)
					c.a[i][j] = (c.a[i][j] + 1ll * x.a[i][k] * y.a[k][j]) % m;
		return c;	
	}
	friend matrix operator + (const matrix &x, const matrix &y){
		matrix c;
		for(int i = 0; i < n; ++i)
			for(int j = 0; j < n; ++j)
				c.a[i][j] = (x.a[i][j] + y.a[i][j]) % m;
		return c;
	}
}mp;	
int ans;
typedef pair<matrix, matrix> pmm;
pmm solve(int k){
	if(k == 1)return pmm(mp, mp); 
	pmm now = solve(k / 2);
	matrix x = now.first, y = now.second;
	matrix ans = x * now.second;
	now.second = now.second * now.second;
	if(k & 1){
		now.second = now.second * mp;
		ans = ans + now.second;
	}
	return pmm(ans + x, now.second);
}
int main(){
	n = read();
	for(int i = 0; i < n; ++i)
		for(int j = 0; j < n; ++j)
			mp.a[i][j] = read();
	k = read(), m = read();
	matrix ma = solve(k - 1).first;
	for(int i = 0; i < n; ++i)ans = (ans + ma.a[i][i]) % m;
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-09-20 17:33  Chen_jr  阅读(107)  评论(7编辑  收藏  举报