“蔚来杯“2022牛客暑期多校训练营7
C Constructive Problems Never Die
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef vector<int> ve;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int T;
int n;
int p[N]; //原数组
int ans[N]; //答案数组
int vis[N]; //标记这个数有没有被放置
ve g[N]; //记录数和出现的位置
void solve(){
cin>>n;
//注意memset比较耗时
for(int i = 1; i <= n; i ++ ){ //清空状态
g[i].clear();
ans[i] = 0;
vis[i] = 0;
}
for(int i = 1; i <= n; i ++ ){
cin>>p[i];
g[p[i]].push_back(i); //g[a[i]]记录了p[i]都在哪个位置出现
}
vector<PII> v; //一维记录数,二维记录数出现的位置
for(int i = 1; i <= n; i ++ ){
if(g[i].size()){ //如果这个数出现过
v.push_back({i, *g[i].begin()}); //记录这个数出现过的第一个位置
}
}
if(v.size() == 1){ //如果只有1个数出现过,无法构成题中所需要的排列,那就直接输出0
cout<<"NO"<<endl;
return ;
}
for(int i = 1; i < v.size(); i ++ ){ //遍历出现过的每个数
ans[v[i].y] = v[i - 1].x; //ans[i]记录第i个位置 = 上一个出现过的数的数值
//按照解释相当于把2放到了1的位置上 因为v只记录了某个数出现的第一个位置,所以这样放一定不会出现重复
vis[v[i - 1].x] = 1; //标记这个数已经被用过了
}
//begin和end是指针,指向位置,back指向最后一个元素,是数值
ans[v.begin()->y] = v.back().x; //把v里面第一个数出现的位置放上最后一个出现的数
vis[v.back().x] = 1; //标记这个数被放过
cout<<"YES"<<endl;
int id = 1; //从第一个数开始,id表示的是放置进排列的数
for(int i = 1; i <= n; i ++ ){
if(ans[i] == 0) { //如果这个位置还没有存过数
while(vis[id] == 1) ++ id; //向后遍历,找到第一个没有被放过位置的数(相当于从小到大放数)
ans[i] = id; //把这个数放置
vis[id] = 1; //标记这个数已经放过
}
}
for(int i = 1; i <= n; i ++ ) cout<<ans[i]<<' ';
cout<<endl;
return ;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>T;
while(T -- ){
solve();
}
}
F Candies
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, x;
int a[N];
int stk[N], top; //模拟栈
int ans; //记录答案
int main()
{
cin>>n>>x;
for(int i = 1; i <= n; i ++ ){
cin>>a[i];
}
for(int i = 1; i <= n; i ++ ){
if(!top) stk[ ++ top] = a[i];
else if(stk[top] == a[i]) top -- , ans ++ ;
else if(stk[top] + a[i] == x) top -- , ans ++ ;
else stk[ ++ top] = a[i];
}
for(int i = 1; i <= top / 2; i ++ ){ //因为有这一步栈内配对,所以只能用手写模拟栈
if(stk[i] == stk[top - i + 1]) ans ++ ;
else if(stk[i] + stk[top - i + 1] == x) ans ++ ;
else break; //一旦有环的两端无法匹配,那剩下的元素就不可能相邻,所以直接退出
}
cout<<ans<<endl;
return 0;
}
G Regular Expression
雨巨说的对,这题最难得确实是读题
正则表达式-B站大佬讲
这题光搞懂正则表达式的匹配机制搞了半天,这里记录一下字符匹配规则
.:英文句号,可以表示任意字符,但不包含换行符
?:表示?的前一个字符需要出现0次或1次,例如:used?可以匹配 use和used
:星号表示前一个字符可以出现0次或多次(注意不能是1次),例如abc可以匹配ac、abbc、abbbbbbc
+:表示可以匹配出现一次及以上次数的字符,比如ab+c可以匹配abc、abbbc、abbbbc
{}:ab{2,6}c表示b可以出现2~6次,ab{2}c表示b可以出现2次
():表示可以把圈起来的字符串看成一个整体被限定符所作用
|: 表示或
明白了这些字符串的意义之后,就知道这是一个分类讨论的题,这里借鉴一下大佬写的题解
#include<bits/stdc++.h>
using namespace std;
int T;
int main()
{
cin>>T;
while(T -- ){
string str;
cin>>str;
int len = str.length();
if(len == 1) cout<<"1 2"<<endl;
else if(len == 2){
if(str[0] == str[1]) cout<<"2 8"<<endl;
else cout<<"2 6"<<endl;
}
else{
bool flag = true;
for(int i = 0; i < len - 1; i ++ ){
if(str[i] != str[i + 1]){ //存在不相同的
flag = false;
break;
}
}
if(!flag) cout<<"2 2"<<endl;
else cout<<"2 4"<<endl;
}
}
return 0;
}
J Melborp Elcissalc
这题怎么说呢,特别不理解,看来雨巨讲题,又看了好几份题解才勉勉强强理解,但是还是感觉太绕了,两个理解思路:
第一个:雨巨的
第二个:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 110;
int dp[N][N][64 * 40];
int n, k, t;
int C[2010][2010]; //表示
int mod = 998244353;
void init(){
for(int i = 0; i <= 210; i ++ ){
for(int j = 0; j <= i; j ++ ){
if(!j) C[i][j] = 1;
else C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
}
signed main()
{
init();
cin>>n>>k>>t;
dp[0][0][0] = 1;
for(int i = 1; i <= k; i ++ ){
for(int j = 0; j <= n; j ++ ){
for(int p = 0; p <= t; p ++ ){
if(dp[i - 1][j][p] == 0) continue;
for(int l = 0; l + j <= n; l ++ ){ //l就是i放的个数,可以一个也不放
//乘上C[j + l][l]是因为:对于已经构造好的前j位前缀和数组来说,又新增了l个位置,从这些位置中选出l个位置
//新选位置的方法与原来前j个序列只放数字(1~i-1)相乘就是新的方案数
if(i == 1){ //当i是1时,上一个状态i就是0,如果这一位放入0,那么区间的增长个数就是C[l + 1][2]
(dp[i][j + l][p + C[l + 1][2]] += dp[i - 1][j][p] * C[j + l][l]) %= mod;
}
else (dp[i][j + l][p + C[l][2]] += dp[i - 1][j][p] * C[j + l][l]) %= mod;
}
}
}
}
cout<<dp[k][n][t]<<endl;
return 0;
}
K Great Party
先推出奇数情况下先手必胜,类比只有一堆时,先手必胜,偶数堆时,就是Nim游戏,如果区间内所有数的异或和是0,那么就是先手必败,否则先手必胜。推出这个结论后,考虑怎么得出在多次询问下得出区间内的异或和的情况,即莫队+异或和前缀数组
大佬写的详细题解1
大佬写的详细题解2
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e6 + 10;
struct Qurey{
int id, l, r;
}q[N];
int sum[2][N];
int n, m;
int a[N];
int len;
int res;
int s[N];
int ans[N];
int get(int x){
return x / len + 1;
}
bool cmp(Qurey a, Qurey b){
int i = get(a.l), j = get(b.l);
if(i != j) return i < j;
return a.r < b.r;
}
int C(int x){
return x * (x - 1) / 2;
}
void add(int x){
res -= C(sum[x % 2][s[x]]);
sum[x % 2][s[x]] ++ ;
res += C(sum[x % 2][s[x]]);
}
void del(int x){
res -= C(sum[x % 2][s[x]]);
sum[x % 2][s[x]] -- ;
res += C(sum[x % 2][s[x]]);
}
signed main()
{
cin>>n>>m;
len = sqrt(n); //分块每个块内的元素数量
for(int i = 1; i <= n; i ++ ){
cin>>a[i];
s[i] = s[i - 1] ^ (a[i] - 1);
}
for(int i = 1; i <= m; i ++ ){
int l, r;
cin>>l>>r;
l -- ;
q[i] = {i, l, r};
}
sort(q + 1, q + m + 1, cmp);
int l = 1, r = 0;
for(int i = 1; i <= m; i ++ ){
int id = q[i].id, L = q[i].l, R = q[i].r;
while(r < R) add( ++ r);
while(r > R) del(r -- );
while(l < L) del(l ++ );
while(l > L) add( -- l);
ans[id] = C(r - l + 1) - res;
}
for(int i = 1; i <= m; i ++ ) cout<<ans[i]<<endl;
return 0;
}
补一场多校真累啊,尤其是今天这个K题,先学了简单的莫队才勉强明白题解的写法,不过也算收获满满,复习博弈论的同时又学了一种算法,下周末网络赛,加油吧!