MX 暑假集训 7.31

【中文字幕】【25時、ナイトコードで。 × MEIKO】幸福刑【2DMV/『世界计划 多彩舞台』主题原创曲】

花了 \(1\) 小时获得了 \(100\) 分的高分并在接下来的 \(3.5\) 小时获得了 \(20\) 分的暴力。

测试点数据造错了先不说,时限从原题的 \(6\) 秒开小到 \(1\) 秒于是 \(n=5\times 10^4\) 把我的 \(O(n\sqrt n \log n)\) 卡了是何意味。

T1: OwenOwl 不学车也不删库

题意

给定 \(p^k\) 枚棋子(\(p\) 为质数且 \(k\) 为正整数),你需要将这些棋子分成若干个组,满足以下条件:

  • 每个组恰有 \(p\) 枚棋子;
  • 每一对棋子至多出现在一个组内。

每枚棋子可以出现在多个组中

求最多能够分出多少个组,并输出方案。

\(2\le p^k\le 2000\)

solution

简单题,就是让我手写 spj 很不爽。

稍微转化一下题意,将一个组看作一个完全图,那么原题等价于求在一个点数为 \(p^k\) 的完全图上最多能找出多少个点数为 \(p\) 的完全子图使得其两两边集无交。

每条边至多分在一个组内,那么答案的上界为 \(\frac{p^k(p^k-1)}{p(p-1)}\)

由于这是一个构造题,并且还给出了 \(p^k\) 这种特殊的点数限制,我们猜想答案一定能够达到这个上界,并尝试进行构造,构造的过程可以看作不断添加点数为 \(p\) 的完全图,最后得到点数为 \(p^k\) 的完全图。

\(p^k\) 这个特殊的点数条件,容易想到可以钦定已经有了 \(p\) 个点数为 \(p^{i}\) 的完全图,我们要在这些完全图中找到若干个不交的完全图并连边形成一个点数为 \(p^{i+1}\) 的完全图。

每个点要向除了它所在完全图内的点以外的所有点连边,所以我们需要找出 \(\frac{p^{i+1}(p^{i+1}-p^i)}{p(p-1)}=p^{2i}\) 个组,每个组各从 \(p\) 个完全图中选出一个点,且不存在两个点同时存在于两个组中。

那么将每个点数为 \(p^i\) 的完全图中的所有点排成一行,每个完全图作为一行,形成一个 \(p\)\(p^i\) 列的矩阵,考虑选定第一行的第 \(s\)\(1\le s\le p^i\))个点为起点,在第 \(j\) 行选定第 \(((s+(j-1)d-1)\mod p^i) +1\) 个数(其中 \(d=0,1,\dots ,p^i-1\)),也就是每行从上一行的数向后数 \(d\) 个数,超出边界就从 \(1\) 开始继续数,由于 \(p\) 是质数,所以容易证明这样的构造一定不会使两个点分在同一组内。

这样共构造出 \(p^i\times p^i=p^{2i}\) 个组,正好满足条件。

\(p^0\) 开始迭代构造即可。

Code
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=3e3+10;
constexpr int inf=1e9+10;

vector<vector<int>> ans;
int n=1,p,k;
int pw[N],id[N][N];

inline void add(vector<int> vec) {ans.push_back(vec);}
inline void solve(int l,int r,int d) {
	int len=pw[d];
	if (d) {for (int i=l;i<=r;i+=len) solve(i,i+len-1,d-1);}
	for (int i=l,t=1;i<=r;i+=len,t++) {
		int L=i,R=i+len-1,now=0;
		f(j,L,R) id[t][now++]=j;
	}
	f(s,0,len-1) f(j,0,len-1) {
		vector<int> tmp;
		int now=s;
		f(i,1,p) {
			tmp.push_back(id[i][now]);
			now=(now+j)%len;
		}
		add(tmp);
	}
}

int main() {
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	scanf("%d%d",&p,&k);
	pw[0]=1;
	f(i,1,k) pw[i]=pw[i-1]*p;
	n=pw[k];
	solve(0,n-1,k-1);
	printf("%d\n",(int)(ans.size()));
	for (vector<int> &vec:ans) {
		for (int x:vec) printf("%d ",x);
		putchar(10);
	}
	return 0;
}

T3: [UOI 2024] Zeroing the segment

posted @ 2026-07-31 17:11  Ff472130  阅读(12)  评论(0)    收藏  举报