2022NOIP A层联测21

A. 反转了

链表搞一下即可

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 500005;
vector<int>g[maxn];
int n, k, h, t;
char c[maxn];
bool vis[maxn];
void link(int u, int v){g[u].push_back(v); g[v].push_back(u);}
int main(){
	freopen("reverse.in","r",stdin);
	freopen("reverse.out","w",stdout);
	n = read(), k = read();
	scanf("%s",c + 1);
	int las = 1, h = 1, t = 1;
	for(int i = 1; i <= k; ++i){
		int x = read();
		if(x > las){
			for(int j = las + 1; j <= x; ++j)link(t, j), t = j;
		}
		las = x; swap(h, t);
	}
	for(int i = las + 1; i <= n; ++i)link(t, i), t = i;
	for(int i = h; i;){
		printf("%c",c[i]);
		vis[i] = true; if(i == t)break;
		for(int v : g[i])if(!vis[v]){i = v; break;}
	}
	printf("\n");
	return 0;
}

B. 学数学

打表找规律 ,发现答案为 \((x, x ^3), (x^3, x^5-2)..\)的链式结构,对每条链进行观察

\(f_i\) 为当前链的第 \(i\) 个元素, \(x\) 为链首, \(f_n = x^2f_{n - 1} - f_{n - 2}\)

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
ll read(){
	ll x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
int cnt = 0;
ll a[10000009];
int main(){
	freopen("math.in","r",stdin);
	freopen("math.out","w",stdout);
	ll n = (ll)1e18;
	for(ll i = 2; i * i * i <= n; ++i){
		ll base = i * i, las = i, now = i * i * i;
		for(; now <= n && now > las; ){
		 	ll k = now; a[++cnt] = now;
			double p = (double)now * base - las; if(p > n)break;
			now = now * base - las; las = k;
		}
	}
	sort(a + 1, a + cnt + 1);
	int t = read();
	for(int i = 1; i <= t; ++i)printf("%ld\n",upper_bound(a + 1, a + cnt + 1, read()) - a);
	return 0;
}

C. 早该砍砍了

\(f_{i, r}\)表示考虑前 \(i\) 个数,确定了最终前 \(r\)个数的方案数

转移看当前数到左右第一个比他小的数之间的区间,枚举之前的右端点更新

发现是前缀和的形式,于是转移过程中记录一下 \(sum\) 即可

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 3005;
const int mod = 1e9 + 7;
int f[maxn][maxn], n, a[maxn];
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
int main(){
	// freopen("cut.in","r",stdin);
	// freopen("cut.out","w",stdout);
	n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
	f[0][0] = 1;
	for(int i = 1; i <= n; ++i){
		for(int j = 0; j <= n; ++j)f[i][j] = f[i - 1][j];
		int l = i, r = i;
		for(l = i; l >= 1 && a[l] >= a[i]; --l); ++l;
		for(r = i; r <= n && a[r] >= a[i]; ++r); --r;
		int now = l ? f[i][l - 1] : 0;
		for(int j = l; j <= r; ++j)add(f[i][j], now), add(now, f[i - 1][j]);
	}
	printf("%d\n",f[n][n]);
	return 0;
}

D. 方格计数

口胡一下,因为太菜了所以.....

容斥

外层容斥非法点,枚举强制选择了哪些非法点

然后套上对角线容斥,钦定某条对角线是否不能选

内层进行行列容斥, \(f_{i, j, k}\) 表示钦定 \(i\)\(j\)列,因为外层容斥有\(k\)个点不能选的容斥系数

这里 \(i, j\) 是钦定了一些行列,最终选择的格子都在这 \(i\)\(j\) 列中

最后通过组合数处理

其实这里是枚举子集,但是直接枚举复杂度无法接受,于是合并了集合大小相同的,就可以用类似背包的形式进行转移

posted @ 2022-11-07 07:01  Chen_jr  阅读(8)  评论(0编辑  收藏  举报