CF diary II

10 题一篇 >o<

1717E. Madoka and The Best University

Difficulty:2200

题意

a+b+c=nlcm(c,gcd(a,b))(1n105)

题解

考虑枚举 gcd(a,b)=i ,设 a+b=ji ,于是需要满足 a=xi,b=yi,gcd(x,y)=1 ,对于每个 i 再枚举 j ,此时对于一组 (i,j) ,所有满足该条件的三元组对应的值为 lcm(nji,i) 。接下来考虑如何计算有多少对满足该条件的 (x,y),考虑 gcd(x,y)=gcd(x,jx)=gcd(j,x)=1 ,那么满足条件的 (x,y) 的数量显然为 φ(j) 。于是对于枚举的每一组 (i,j) ,其对答案的贡献为 lcm(nji,i)φ(j) 。直接计算即可,复杂度 O(nlogn)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 100010;

LL N, v[maxn], prime[maxn], phi[maxn];

LL phis(LL n)
{
	phi[0] = 0, phi[1] = 1;
	LL m = 0;
	for (LL i = 2; i <= n; i++)
	{
		if (!v[i])
		{
			v[i] = i;
			prime[++m] = i;
			phi[i] = i - 1;
		}
		for (LL j = 1; j <= m; j++)
		{
			if (prime[j] > v[i] || prime[j] > n / i)
				break;
			v[prime[j] * i] = prime[j];
			if (i % prime[j] == 0)
				phi[i * prime[j]] = phi[i] * prime[j];
			else
				phi[i * prime[j]] = phi[i] * phi[prime[j]];
		}
	}

	return m;
}

LL gcd(LL a, LL b)
{
	if (!b)
		return a;
	return gcd(b, a % b);
}

LL lcm(LL a, LL b)
{
	return a / gcd(a, b) * b;
}

void solve()
{
	phis(N);
	LL ans = 0;
	for (LL i = 1; i <= N; i++)
	{
		for (LL j = 2; j * i <= N; j++)
			ans = (ans + lcm(N - j * i, i) * phi[j] % MOD) % MOD;
	}
	cout << ans << endl;
}

int main()
{
	IOS, cin >> N, solve();
	return 0;
}

1336C. Kaavi and Magic Spell

Difficulty:2200

题意

一个串 s ,不断删去其第一个字符,加入新的串 aa 一开始为空串,加入时可以选择两种操作,从最左侧加入或者从最右侧加入(空串时也算作两种不同的操作),考虑有多少种不同的操作序列(可以操作任意步),使得 a 的前缀是另外一个串 t

题解

考虑区间 dp , 记 fi,j 为当前串恰好为 t[i:j] 的方案数,如果 t 的长度小于 s ,可以视作 t 后面有若干通配符,之后转移非常显然,结果为 i=|t||s|f1,i

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 3010;

string S, T;
LL N, M, f[maxn][maxn];

void solve()
{
	S = ' ' + S, T = ' ' + T;
	for (LL i = 1; i <= N; i++)
	{
		if (i > M || S[1] == T[i])
			f[i][i] = 2;
	}
	for (LL i = 2; i <= N; i++)
	{
		for (LL l = 1; l + i - 1 <= N; l++)
		{
			LL r = l + i - 1;
			if (r > M || S[i] == T[r])
				f[l][r] = (f[l][r] + f[l][r - 1]) % mod;
			if (l > M || S[i] == T[l])
				f[l][r] = (f[l][r] + f[l + 1][r]) % mod;
		}
	}
	LL ans = 0;
	for (LL i = M; i <= N; i++)
		ans = (ans + f[1][i]) % mod;
	cout << ans << endl;
}

int main()
{
	IOS;
	cin >> S >> T;
	N = S.length(), M = T.length();
	solve();

	return 0;
}

1479C. Continuous City

Difficulty:2500

题意

最多使用 32 个节点,构造一张 DAG ,不能有重边,每条边必须为编号小的节点指向大的节点,各边权值 [1,106] ,使得 1n 恰好有 RL+1 条路径,其长度分别为 LR

题解

考虑这样一个构造方法,我们固定使用 23 个节点,然后对于每个 i>1 ,我们向 22j 连一条权值为 2j 的边直到 i+1 。这样的话,从每个 i 为起点到 n 的路径恰为长度为 1222i 路径各一条,特殊的,从 23 开始则为 0 ,于是我们可以将 [L,R] 转化为以 0 开始,之后对右端点每个二进制位进行考虑,从 1 连边即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 19650827;
const int maxn = 510;

int L, R;
struct Edge {
	int from, to, cost;
}E[maxn];
int nums[30];

void solve()
{
	cout << "YES" << endl;
	int v = 23, e = 0;
	for (int i = 2; i <= 22; i++)
	{
		e++;
		E[e] = Edge{ i,23,1 };
	}
	for (int i = 0; i < 20; i++)
	{
		for (int j = 1; j <= i + 1; j++)
		{
			e++;
			E[e] = Edge{ 21 - i,21 - i + j,1 << (i - j + 1) };
		}
	}
	int x = L;
	int tmp = x;
	R -= L;
	E[++e] = Edge{ 1,23,tmp };
	for (int i = 19; i >= 0; i--)
	{
		if ((R >> i) & 1)
		{
			e++;
			E[e] = Edge{ 1,22 - i,tmp };
			tmp += 1 << i;
		}
	}
	cout << v << ' ' << e << endl;
	for (int i = 1; i <= e; i++)
		cout << E[i].from << ' ' << E[i].to << ' ' << E[i].cost << endl;
}

int main()
{
	IOS;
	cin >> L >> R;
	solve();

	return 0;
}

1368E. Ski Accidents

Difficulty:2500

题意

一张 n(1n2105) 个顶点的 DAG (可能有重边,每个节点出度 2 )。现在标记不超过 47n 个顶点,被标记顶点关联的边将被删除,给出一个标记方案,使得最终的图中任意路径长度 1

题解

神仙构造题,反正我是肯定想不出来。。。
考虑将图中的节点划分为三个集合 A,B,C ,并且满足 |C|2|B|4|A| ,然后我们标记 C 中的所有点,这样点数一定不会超出限制,接下来考虑具体的划分方案,我们将入边为空或者仅来自 C 中的点的节点划入集合 A 。将入边仅来自 C 中的点或来自 A|C 的点的节点划入集合 B ,其他节点划入集合 C ,显然该划分合法且由出度 2 的性质容易证明三个集合满足最初的性质,我们删除 C 后,由于 A 集合没有来自 B 集合的边, 集合内部互相没有连边,因此路径长度都小于 1 ,如此构造即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 200010;

int T, N, M, in[maxn], col[maxn], s[maxn];
vector<int>G[maxn], C;

void add_edge(int from, int to)
{
	G[from].push_back(to);
}

void solve()
{
	queue<int>que;
	for (int i = 1; i <= N; i++)
	{
		if (!in[i])
			que.push(i);
	}
	while (!que.empty())
	{
		int v = que.front(); que.pop();
		if (col[v] == 0 || col[v] == 4)
			s[v] = 1;
		else if (col[v] == 1 || col[v] == 5)
			s[v] = 2;
		else
			C.push_back(v), s[v] = 3;
		for (auto& to : G[v])
		{
			col[to] |= (1 << (s[v] - 1)), in[to]--;
			if (!in[to])
				que.push(to);
		}
	}
	cout << C.size() << endl;
	for (auto& v : C)
		cout << v << ' ';
	cout << endl;
}

int main()
{
	IOS;
	cin >> T;
	while (T--)
	{
		cin >> N >> M;
		C.clear();
		for (int i = 1; i <= N; i++)
			G[i].clear(), in[i] = s[i] = col[i] = 0;
		int u, v;
		for (int i = 1; i <= M; i++)
			cin >> u >> v, add_edge(u, v), in[v]++;
		solve();
	}

	return 0;
}

1158C. Permutation recovery

Difficulty:2200

题意

一个长为 n(1n5105) 丢失了的排列 p ,现在你知道每个位置右边最近的大于它的元素位置 nxti ,特别的,如果不存在这样的位置, nxti=n+1 。但是 nxt 数组也有一部分缺失了,它们的值记为 1 ,找出一个合法的排列或者输出无解。

题解

考虑无解的情况,当且仅当存在 (i,j) ,使得 i<j<nxti<nxtj 时会无解,其他情况只要让 1 的位置都设成 nxti=i+1 即可保证不出现无解情况,接下来考虑如何构造一组解,我们考虑第一个 nxti=n+1 的位置,显然其左侧以及右侧的数字均不超过它,于是它就是整个排列的最大值,我们考虑 dfs 递归地求解,记 dfs(l,r,x) 为求解区间 [l,r] ,元素为 [xr+l,x] 时的情况,根据上面的分析,我们显然可以把 [1,i1] 递归求解,安排尽可能大的值,然后对于 [i+1,n] 这一部分,依然发现这一部分第一个 nxti=n+1 的位置为当前段的最大值,于是预处理出每个 nxti=n+1i 即可将 [1,n] 分割为若干段,字段的求解类似,将 n+1 对应替换为 r+1 即可。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc p*2
#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 500010;

int T, N, nxt[maxn], A[maxn], dat[maxn];
vector<int>G[maxn];

void add(int i, int x)
{
	while (i <= N + 1)
		dat[i] += x, i += i & (-i);
}

int sum(int i)
{
	int ret = 0;
	while (i)
		ret += dat[i], i -= i & (-i);
	return ret;
}

void dfs(int l, int r, int x)
{
	if (l > r)
		return;
	if (l == r)
	{
		A[l] = x;
		return;
	}
	int lst = l - 1;
	for (auto& i : G[r + 1])
		A[i] = x, dfs(lst + 1, i - 1, x - 1), x -= i - lst, lst = i;
}

void solve()
{
	for (int i = 1; i <= N; i++)
	{
		if (nxt[i] == -1)
			nxt[i] = i + 1;
		else
		{
			if (sum(nxt[i] - 1) - sum(i))
			{
				cout << -1 << endl;
				return;
			}
			add(nxt[i], 1);
		}
		G[nxt[i]].push_back(i);
	}
	dfs(1, N, N);
	for (int i = 1; i <= N; i++)
		cout << A[i] << ' ';
	cout << endl;
}

int main()
{
	IOS;
	cin >> T;
	while (T--)
	{
		cin >> N;
		for (int i = 1; i <= N; i++)
			cin >> nxt[i], dat[i] = 0, G[i].clear();
		dat[N + 1] = 0, G[N + 1].clear();
		solve();
	}

	return 0;
}

1036F. Relatively Prime Powers

Difficulty:2400

题意

t(1t105) 次询问,求 [2,n](2n1018) 中有多少数,其各个质因数的指数的 gcd1

题解

看到 gcd 相关计数可以考虑莫比乌斯反演,我们记 f(x) 为各质因数指数 gcdx 的数字的数量, g(x) 为各质因数指数 gcdx 的倍数数字的数量,显然 g(x)=x|df(d) ,根据莫反的形式二,我们有:

g(x)=x|df(d)f(x)=x|dμ(dx)g(d)

f(1)=dμ(d)g(d) 即为所求,考虑如何计算 g(d) ,考虑如果一个数各质因数指数的 gcdd 的倍数,一定可以写成 bd 的形式,也就是说,求出最大的满足 bdnb 即可,即 g(d)=dx1 ,注意 1 是为了不用将 1 算进去,然后直接开根卡精度, d 只要枚举到 log2n 即可,开根适当大一点最后再快速幂 check 往回减。

代码

view code
TLE

1401F. Reverse and Swap

Difficulty:2400

题意

4 种操作:
1 x k ax=k
2 k2k 个元素作为字段,将所有子段分别翻转
3 k2k 个元素作为字段,相邻两个字段互换。
4 l r 输出 i=lrai
2n(0n18) 个元素, q(1q105) 次操作。

题解

注意到各划分的子段一定是一棵线段树中的某一节点,之后就比较好处理, 14 操作不用多说, 2 操作需要将 k 层以下各节点都打上翻转标记, 3 操作需要将 k+1 层各节点都打上翻转标记。一开始考虑怎么打这些标记很久,后来发现所有的标记都是整层地打,于是我们只需要为每一层记录标记,然后也不用下传标记实质性地改动树的结构,查询/修改时如果当前节点所在层被打上标记,那么将左子树视为右子树,右子树视为左子树即可,复杂度 O(nq)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mk make_pair
//#define int LL
//#define lc tr[p].ch[0]
//#define rc tr[p].ch[1]
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 300010;

LL N, Q, A[maxn];
struct Node {
	int ch[2], k;
	LL sum;
}tr[maxn * 4];
int rev[20];

void pushup(int p)
{
	int lc = tr[p].ch[0], rc = tr[p].ch[1];
	tr[p].sum = tr[lc].sum + tr[rc].sum;
}

int tot = 0, rk = 0;

int build(int x)
{
	int p = ++tot;
	tr[p].k = x;
	if (x)
		tr[p].ch[0] = build(x - 1), tr[p].ch[1] = build(x - 1), pushup(p);
	else
		tr[p].sum = A[++rk];
	return p;
}

void modify(int p, int x, LL val)
{
	if (!tr[p].k)
	{
		tr[p].sum = val, A[x] = val;
		return;
	}
	int lc = tr[p].ch[0], rc = tr[p].ch[1];
	if (rev[tr[p].k])
		swap(lc, rc);
	int mid = 1 << (tr[p].k - 1);
	if (x <= mid)
		modify(lc, x, val);
	else
		modify(rc, x - mid, val);
	pushup(p);
}

LL query(int p, int l, int r)
{
	if (l == 1 && ((1 << (tr[p].k)) == r) || tr[p].k == 0)
		return tr[p].sum;
	int mid = 1 << (tr[p].k - 1), lc = tr[p].ch[0], rc = tr[p].ch[1];
	if (rev[tr[p].k])
		swap(lc, rc);
	if (r <= mid)
		return query(lc, l, r);
	else if (l > mid)
		return query(rc, l - mid, r - mid);
	else
		return query(lc, l, mid) + query(rc, 1, r - mid);
}

void solve()
{
	build(N);
	int op, x, k, l, r;
	while (Q--)
	{
		cin >> op;
		if (op == 1)
			cin >> x >> k, modify(1, x, k);
		else if (op == 3)
			cin >> k, rev[k + 1] ^= 1;
		else if (op == 2)
		{
			cin >> k;
			for (int i = k; i; i--)
				rev[i] ^= 1;
		}
		else
			cin >> l >> r, cout << query(1, l, r) << endl;
	}
}

int main()
{
	IOS;
	cin >> N >> Q;
	for (int i = 1; i <= (1 << N); i++)
		cin >> A[i];
	solve();

	return 0;
}

1041F. Ray in the tube

Difficulty:2500

题意

两条平行于 x 轴的直线,直线上有若干带命中的整点,现在从下面直线选择任意一个整点(可以不是带命中的点),射出激光,击中直线后会反弹,并且反弹点必须为整点,求激光可以命中的最大待命中整点数。

题解

不太好想的思维题,考虑激光反弹一次后是一个三角形,底边为 2x 。可以发现,如果 x 有任意大于 1 的奇约数 y ,则底边长为 2xy 的激光可以经过的点是之前激光的超集。于是选择的 x 必须没有大于 1 的奇约数,也就是 x=2k 。有了这个结论后就十分显然了,枚举 k ,考虑起始点位于各 2k 模数处的最大值即可,用 map 维护,复杂度 O(nlognlog109)

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair < int, int > PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 100010;
const int maxm = 65;

int N, X, M, Y, A[maxn], B[maxn];
map<int, int>mp;

void solve()
{
	int ans = 0;
	for (int i = 1; i <= N; i++)
		mp[A[i]]++;
	for (int i = 1; i <= M; i++)
		mp[B[i]]++;
	for (auto& [x, y] : mp)
		ans = max(ans, y);
	for (int j = 0; j < 30; j++)
	{
		mp.clear();
		for (int i = 1; i <= N; i++)
			mp[A[i] % (1 << j + 1)]++;
		for (int i = 1; i <= M; i++)
			mp[(B[i] + (1 << j)) % (1 << j + 1)]++;
		for (auto& [x, y] : mp)
			ans = max(ans, y);
	}
	cout << ans << endl;
}

int main()
{
	IOS;
	cin >> N >> X;
	for (int i = 1; i <= N; i++)
		cin >> A[i];
	cin >> M >> Y;
	for (int i = 1; i <= M; i++)
		cin >> B[i];
	solve();

	return 0;
}

1481E. Sorting Books

Difficulty:2500

题意

n(1n5105) 本书,每次操作可以选择任意一本书放到书架的末尾,每本书有颜色 ai(1ain) 。当所有颜色的书都被放在一起时,停止操作,求最少操作数。

题解

显然每本书至多被挪动一次,我们考虑反向来求解整个书架最多有多少书不需要移动,记 fi 为从 i 开始的后缀中最多有多少书不被移动。考虑到第 i 个位置时,如果其需要移动}$ ,如果其不动,那么有两种情况,首先如果其是其颜色的左端点,那么如果它不动并且最后还要满足要求,那么就应当让从左端点到右端点区间内所有其他颜色移动,于是 fi=cntai+frai+1cnt 记录的是当前一个后缀的信息,如果不是左端点,那么如果要让其颜色在一起,就得让在它前面的颜色相同的都放到后面再把中间颜色不同的挪走,也就是后缀中所有颜色不同的都得挪,于是 fi=cntai ,最后所有转移取 max 即可。
最后答案为 nf1
后两种转移也可以理解为尝试 [i:rai] 内所有颜色为 ai 的书不动时的结果。(因为自己不动,其后面的就不能动)。

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair < int, int > PII;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 500010;
const int maxm = 65;

int N, A[maxn], f[maxn], l[maxn], r[maxn], cnt[maxn];
bool vis[maxn];

void solve()
{
	for (int i = 1; i <= N; i++)
	{
		if (!vis[A[i]])
			vis[A[i]] = true, l[A[i]] = i;
	}
	mst(vis, 0);
	for (int i = N; i >= 1; i--)
	{
		if (!vis[A[i]])
			vis[A[i]] = true, r[A[i]] = i;
	}
	for (int i = N; i >= 1; i--)
	{
		cnt[A[i]]++;
		if (i == l[A[i]])
			f[i] = max(f[i], cnt[A[i]] + f[r[A[i]] + 1]);
		else
			f[i] = max(f[i], cnt[A[i]]);
		f[i] = max(f[i], f[i + 1]);
	}
	cout << N - f[1] << endl;
}

int main()
{
	IOS;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> A[i];
	solve();

	return 0;
}

1034B. Little C Loves 3 II

Difficulty:2200

题意

nm(1n,m109) 的棋盘,每次放置一对棋子,两者曼哈顿距离恰好为 3 ,最多能放多少棋子。

题解

分类讨论找规律思维题。
不妨设 n<m
显然当 n+m4 时放不了,接下来考虑 n=1 ,这也很好判断,顺着放即可,找出规律即可计算。
之后考虑 n=2 ,发现 m=3m=7 有两个空位, n=4n=5n=6 可以放满,其他的 m 也都可以由 5,6,7 生成,所以必然放满。
接下来考虑 n=3,我们发现 m=3 时有仅空出中间的放 8 个的方案,我们用这一特点,在 33 后不断添加 32 ,使得 m 为奇数时一定只有一个放不满,我们在 n 方向亦可以扩展,先添加 23 ,之后不断向 m 方向添加 22 ,亦仅有一个放不满,于是有当 n,m 都为奇数时,答案为 nm1
n,m 都为偶数时,可以全部由 2 扩展填满,答案为 nm
n,m 一奇一偶时,与都为奇数类似构造,区别是最后仅放置一个空列,发现也可以填满,于是答案也为 nm

代码

view code
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair < int, int > PII;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const double eps = 1e-8;
const LL mod = 1000000007;
const LL MOD = 998244353;
const int maxn = 500010;
const int maxm = 65;

LL N, M;

void solve()
{
	if (N + M <= 4)
	{
		cout << 0 << endl;
		return;
	}
	if (N > M)
		swap(N, M);
	if (N == 1)
	{
		LL t = max(0LL, M % 6 - 3);
		cout << (M / 6 * 6 + t * 2) << endl;
		return;
	}
	else if (N == 2)
	{
		if (M == 3)
			cout << 4 << endl;
		else if (M == 7)
			cout << 12 << endl;
		else
			cout << M * 2 << endl;
	}
	else
	{
		if (N % 2 && M % 2)
			cout << N * M - 1 << endl;
		else
			cout << N * M << endl;
	}
}

int main()
{
	IOS;
	cin >> N >> M;
	solve();

	return 0;
}

本文作者:Prgl

本文链接:https://www.cnblogs.com/Prgl/p/16651626.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Prgl  阅读(20)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开