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\) 列中
最后通过组合数处理
其实这里是枚举子集,但是直接枚举复杂度无法接受,于是合并了集合大小相同的,就可以用类似背包的形式进行转移