牛客多校第七场个人补题C F G J K

牛客多校第七场个人补题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 ai1的异或和,然后我们可以对异或取前缀和,当不同位置相同时就是这一段异或和为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();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值