牛客多校第七场个人补题C F G J K
C
题意
给一个数列,问是否存在一个排列使其对应位置的数不相同
思路
比赛的时候脑子抽了,从大模拟想到随机,一直都想复杂了,后来才意识到我们可以直接写一个序列出来,然后看这时候匹配的个数的奇偶,若为奇数则两两交换,再有一个三个的大交换(特判1的时候,再扫一遍看有没有解就行了),偶数的时候直接两两交换就可以达到目的,不明白比赛时为啥想了那么久。。。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int a[N];
int res[N];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
res[i] = i;
}
srand(time(NULL));
int pre = -1;
int cnt = 0;
int p1=0;
for (int i = 1; i <= n; i++) {
if (res[i] == a[i]) {
if (pre == -1) pre = i;
else swap(res[pre], res[i]), pre = -1, cnt++,p1=i;
}
}
if(cnt&&pre==-1) {
}
else if(cnt&&pre!=-1) {
swap(res[pre],res[p1]);
}
else if(cnt==0&&pre!=-1) {
bool f=0;
for(int i=1;i<=n;i++) {
if(a[i]!=res[pre]) {
swap(res[i],res[pre]);
f = 1;
break;
}
}
if(!f) {
cout<<"NO\n";
return;
}
}
cout<<"YES\n";
for (int i = 1; i <= n; i++) cout << res[i] << " \n"[i == n];
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
F
题意
一个环形序列,相邻的两个若满足相同的条件或和为一个特定值则可以消除,最后问最多可以消除多少次
思路
看这题的时候我们可以意识到消除次数最多是 n 2 \frac{n}{2} 2n次,所以直接模拟不会超时,当时想的是进行O(1)的删除,所以写了一个循环链表。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
struct node {
int val;
node *next;
node *pre;
} a[N];
void erase(node *x) {
x->pre->next = x->next->next;
x->next->next->pre = x->pre;
}
void solve() {
int n, x;
cin >> n >> x;
for (int i = 0; i < n; i++) cin >> a[i].val;
for (int i = 0; i < n - 1; i++) {
a[i].next = a + i + 1;
}
for (int i = 1; i < n ; i++) {
a[i].pre = a + i - 1;
}
a[n - 1].next = a;
a[0].pre = a + n - 1;
int cnt = 0;
node *i = a;
int res = 0;
bool f=0;
for (; cnt <= 100 * n&&res< n/2; i = i->next) {
while (i->val == i->next->val || i->val + i->next->val == x) {
erase(i);
i = i->pre;
res++;
if(res*2>=n) {f=1;break;}
}
if(f) break;
cnt++;
}
cout << res << endl;
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
G
题意
给一个字符串,问其最短的正则表达式长度为几,有多少种
思路
读完题意识到其实对于括号问号等符号只会加长度,真正有用的就只有’*‘,’+‘,’.'符号,然后推一推就发现就是个trick。。。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
void solve() {
string x;
cin>>x;
map<char,int>mp;
for(int i=0;x[i];i++) {
mp[x[i]]++;
}
if(mp.size()==1) {
if(x.length()==1) cout<<1<<' '<<2<<endl;
else if(x.length()==2)cout<<2<<' '<<8<<endl;
else cout<<2<<' '<<4<<endl;
}
else if(x.length()==1) cout<<1<<' '<<2<<endl;
else if(x.length()==2)cout<<2<<' '<<6<<endl;
else cout<<2<<' '<<2<<endl;
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
J
题意
求对于由0~k-1组成长度为n的数组子序列为k的倍数恰好有t个的组合数量。
思路
对于一个前缀和来说,由于我们的数组每个元素取值范围是0~k-1,意味着其实我们可以用前缀和数组来唯一的表示每个元素,所以就可以用一个三维dp [ i ] [ j ] [ k]来表示整个数组的组合数量,一维表示用前i个数,一维表示对于长度为j的数组,最后一维表示所存在的数量,最后输出dp [ k ] [ n ] [ t ]就完了(实在不明白为啥可以想到这里,另外貌似如果换一个更大的范围,比如0~2(k-1)其实可以对dp求组合数求解?)
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 1e6 + 10;
const int mod = 998244353;
typedef pair<int, int> PII;
int dp[70][70][4000];
int c[70][70];
void init() {
for (int i = 0; i <= 70; i++) {
c[i][0] = 1;
}
for (int i = 1; i < 70; i++) {
for (int j = 1; j < 70; j++) {
(c[i][j] = c[i - 1][j] + c[i - 1][j - 1]) %= mod;
}
}
}
void solve() {
init();
int n, k, t;
cin >> n >> k >> t;
dp[0][0][0] = 1;
for (int i = 1; i <= k; i++) {
if (i == 1) {
for (int len = 0; len <= n; len++) {
for (int s = 0; s <= t; s++) {
for (int num = 0; len + num <= n; num++) {
if (dp[i - 1][len][s] == 0)continue;
(dp[i][len + num][s + c[num + 1][2]] += dp[i - 1][len][s] * c[num + len][num]) %= mod;
}
}
}
} else {
for (int len = 0; len <= n; len++) {
for (int s = 0; s <= t; s++) {
for (int num = 0; len + num <= n; num++) {
if (dp[i - 1][len][s] == 0)continue;
(dp[i][len + num][s + c[num][2]] += dp[i - 1][len][s] * c[num + len][num]) %= mod;
}
}
}
}
}
cout << dp[k][n][t] << endl;
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
K
题意
两个人玩nim游戏升级版(加了一条每次操作可以合并两堆石子的规则),给q次询问,每次有一个区间,求对每个区间有多少种先手必胜的子区间
思路
比赛的时候读错了题,以为是在一个区间内删除一个子区间仍然必胜,现在来看的话主要是当时没有找到必胜策略,其中奇数堆的时候必然先手必胜,偶数堆的时候其实就是不能转化到[1…1]的状态,其实就和nim游戏很像了(nim终态是[0…0]),那么就应该是求 a i − 1 a_i-1 ai−1的异或和,然后我们可以对异或取前缀和,当不同位置相同时就是这一段异或和为0,最后就是可以离线一下询问使用莫队就过了。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int sq;
struct query { // 把询问以结构体方式保存
int l, r, id;
bool operator<(const query &o) const { // 重载<运算符,奇偶化排序
// 这里只需要知道每个元素归属哪个块,而块的大小都是sqrt(n),所以可以直接用l/sq
if (l / sq != o.l / sq)
return l < o.l;
if (l / sq & 1)
return r < o.r;
return r > o.r;
}
} qq[N];
int a[N];
int res[N];
int sum;
int cnt[2][N*4];
int ans[N];
inline void add(int p) {
sum -= cnt[p % 2][res[p]] * (cnt[p % 2][res[p]] - 1) / 2;
cnt[p % 2][res[p]]++;
sum += cnt[p % 2][res[p]] * (cnt[p % 2][res[p]] - 1) / 2;
}
inline void del(int p) {
sum -= cnt[p % 2][res[p]] * (cnt[p % 2][res[p]] - 1) / 2;
cnt[p % 2][res[p]]--;
sum += cnt[p % 2][res[p]] * (cnt[p % 2][res[p]] - 1) / 2;
}
void solve() {
int n, q;
sum = 0;
cin >> n >> q;
sq = sqrt(q);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) res[i] = res[i - 1] ^ (a[i] - 1);
for (int i = 1; i <= q; i++) {
cin >> qq[i].l >> qq[i].r;
qq[i].l--;
qq[i].id = i;
}
sort(qq + 1, qq + q + 1);
int l = 1, r = 0;
for (int i = 1; i <= q; i++) {
while (l > qq[i].l) add(--l);
while (r < qq[i].r) add(++r);
while (l < qq[i].l) del(l++);
while (r > qq[i].r) del(r--);
ans[qq[i].id] = (qq[i].r - qq[i].l + 1) * (qq[i].r - qq[i].l ) / 2 - sum;
}
for (int i = 1; i <= q; i++) {
cout << ans[i] << "\n";
}
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}