2022NOIPA层联测8

A. tothecrazyones

先考虑 \(a_i < x + y\) 的,那么两个人必然至多只有一个人能取同一堆石头,那么对于先手来说,取的越多越好,如果先手把能取的都取了,后手还能取,那么后手必胜,否则先手必胜

考虑一般情况,发现如果先手/后手希望保持某个状态,那么对方取完 $x $ 或 $ y$ , 自己可以跟着取 \(y\)\(x\),相当于给 \(a_i\) 减去 \(x + y\) 不难发现此时答案不变,于是就可以对 \(x + y\) 取模,化归到第一种情况

code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>

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;
ll a[maxn], n, x, y;
void solve(){
	n = read(), x = read(), y = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)a[i] %= (x + y);
	int fl = 1;
	if(x <= y){
		fl = 0;
		for(int i = 1; i <= n; ++i)if(a[i] >= x){fl = 1; break;}
	}else{
		fl = 0;
		for(int i = 1; i <= n; ++i)if(a[i] >= x){fl = 1; a[i] -= x;}
		if(fl)for(int i = 1; i <= n; ++i)if(a[i] >= y){fl = 0; break;}
	}
	printf("%d\n",fl);
}
int main(){
	int t = read();
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

B. vmefifty (CF1699D)

这考试原题我没发现,打了一个比原来复杂不少的代码。。。

\(f_i\) 表示考虑前 \(i\) 个,删到只剩 \(a_i\) 同色的,最多剩下多少

于是 \(f_i = max(f_j + 1 - cost[j + 1, i - 1])\)

其中 \(j\) 为所有 \(j < i, a_j == a_i\) \(cost\)为需要多少颜色为 \(a_i\) 的消去区间内的

然后那就是区间出现主元素时为 \(e + e - len\) 其他时候为 \(0\), 使用摩尔投票+桶预处理一下

注意区间长度为奇数时 \(cost\) 至少为 \(1\)

code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cassert>

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 = 2055;
int f[maxn], cnt[maxn][maxn], mx[maxn][maxn];
int t, a[maxn], n, b[maxn];
vector<int>pos[maxn];
void solve(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)b[i] = a[i];
	sort(b + 1, b + n + 1);
	int up = unique(b + 1, b + n + 1) - b - 1;
	for(int i = 1; i <= n; ++i)a[i] = lower_bound(b + 1, b + up + 1, a[i]) - b;
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= up; ++j)cnt[i][j] = cnt[i - 1][j];
		++cnt[i][a[i]];
	}
	for(int l = 1; l <= n; ++l){
		int las = -1, count = 0;
		for(int r = l; r <= n; ++r){
			if(count == 0)las = a[r], count = 1;
			else if(las != a[r])--count;
			else ++count;
			mx[l][r] = max(0, (cnt[r][las] - cnt[l - 1][las]) * 2 - (r - l + 1));
			if((r - l + 1) & 1){
				mx[l][r] = max(1, mx[l][r]);
			}
		}
	}
	for(int i = 1; i <= up; ++i)pos[i].push_back(0);
	for(int i = 1; i <= n; ++i)pos[a[i]].push_back(i);
	for(int i = 1; i <= up; ++i)pos[i].push_back(n + 1);
	for(int i = 1; i <= n + 1; ++i)f[i] = -maxn;
	f[0] = 0; int ans = 0;
	for(int i = 1; i <= up; ++i){
		for(int now : pos[i]){
			for(int las : pos[i]){
				if(las == now)break;
				f[now] = max(f[now], f[las] - mx[las + 1][now - 1] + 1);
			}
		}
	}
	for(int i = 1; i <= n; ++i)pos[i].clear();
	printf("%d\n",n - f[n + 1] + 1);
}
int main(){
	t = read();
	for(int i = 1; i <= t; ++i)solve();
	return 0;
}

C. wishusgoodluck(CF662C)

学习了 \(fwt\)

题解讲的很好。懒了

image

code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxl = 100005;
const int maxn = 1048579;
const int mod = 15620947;
const int inv2 = 7810474;
int cnt[1148576], len;
void fwt_xor(int f[], int opt){
	opt = opt == -1 ? inv2 : 1;
	for(int l = 2, k = 1; l <= len; l <<= 1, k <<= 1)
		for(int i = 0; i < len; i += l)
			for(int j = 0; j < k; ++j){
				int x = f[i + j], y = f[i + j + k];
				f[i + j] = 1ll * opt * (x + y) % mod;
				f[i + j + k] = 1ll * opt * (x - y + mod) % mod;
			}
}

int n, m;
char mp[25][maxl];
int a[maxl];
int f[maxn], g[maxn];

int main(){
	scanf("%d%d",&n, &m);
	for(int i = 1; i <= n; ++i)scanf("%s",mp[i] + 1);
	for(int i = 1; i <= m; ++i){
		for(int j = 1; j <= n; ++j)a[i] |= ((mp[j][i] == '1') << (j - 1));
	}
	len = 1 << n;
	for(int i = 1; i < len; ++i)cnt[i] = cnt[i >> 1] + (i & 1);
	for(int i = 0; i < len; ++i)g[i] = min(cnt[i], n - cnt[i]);
	for(int i = 1; i <= m; ++i)++f[a[i]];
	fwt_xor(f, 1); fwt_xor(g, 1);
	for(int i = 0; i < len; ++i)f[i] = 1ll * f[i] * g[i] % mod;
	fwt_xor(f, -1);
	int ans = 0x3f3f3f3f;
	for(int i = 0; i < len; ++i)ans = min(ans, f[i]);
	printf("%d\n",ans);
	return 0;
}

D. xiongaktenioi(CF1034C)

如果把树砍成 \(k\) 份,那么每块大小为 \(s/k\) ,而且划分方案唯一

断开了某个点与其父亲,当且仅当 \(s_i \equiv 0 \mod s / k]\)

当这样的点存在 \(k\) 个时,把树划分成 \(k\) 份就是合法的

设 $f_k =\sum_{i = 1}^n [s_i \equiv 0 \mod s / k] $

考虑快速求出 \(f_k\) , 反过来考虑 \(s_i\) 的贡献

\(t = s / k\)

那么 \(k = s / t\)

\(s_i \equiv 0 \mod t\) 等价 \(s_i = at\)

上式均为整数,于是 \(t | s_i\) \(t |s\),那么 \(t | gcd(s, s_i)\)

也就是 \((s/ k) | gcd(s, s_i)\)\((s/ gcd(s, s_i) )| k\)

我们只需要 \(k <= n\) 的贡献,于是开桶枚举倍数即可快速求解 \(f_k\)

然后考虑求方案,令 \(g_i = [f_i == i]\)

\(g_i\) 表示划分成 \(i\) 块的方案数

那么能划分成 \(i\) 块,并且能够划分成 \(ai\) ,就有了先划分 \(i\) 块,再划分 \(ai\) 块的方案

于是从小到大去他的 合法倍数 处贡献

code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
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 = 1000005;
const int mod = 1e9 + 7;
int n, a[maxn], fa[maxn];
ll si[maxn];
int f[maxn], cnt[maxn], g[maxn];
int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 2; i <= n; ++i)fa[i] = read();
	for(int i = n; i >= 1; --i){si[i] += a[i]; si[fa[i]] += si[i];}
	for(int i = 1; i <= n; ++i){
		ll now = si[1] / __gcd(si[i], si[1]);
		if(now <= n)++cnt[now];
	}
	for(int i = 1; i <= n; ++i)if(cnt[i])
		for(int j = i; j <= n; j += i)
			f[j] += cnt[i];
	for(int i = 1; i <= n; ++i)f[i] = f[i] == i;
	for(int i = 1; i <= n; ++i)g[i] = f[i];
	int ans = 1;
	for(int i = 2; i <= n; ++i){
		ans = (ans + g[i]) % mod;
		for(int j = i + i; j <= n; j += i)if(f[j])g[j] = (g[j] + g[i]) % mod;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-10-14 18:01  Chen_jr  阅读(54)  评论(0编辑  收藏  举报