2025 暑假集训 Day1

2025.8.4

Day 1 比赛

四题两个 Ad-hoc ... 总分 \(10+0+50+20=70\) 排名 \(31/50\) 炸飞了

难度应该是黄蓝蓝绿(?)你说得对但是我 T1 赛场上没想到搜索,吐了

A

给定一个自然数 \(N\),找出一个 \(M\),使得 \(M > 0\)\(M\)\(N\) 的倍数,并且 \(M\)\(10\) 进制表示只包含 \(0\)\(1\)。求最小的 \(M\);如果无解,则输出 \(-1\)\(N \le 10^6\)

样例输入 4 样例输出 100

场上写了个暴力准备骗 \(N \le 10^3\) 的分结果写挂了 /yun

正解是 BFS,考虑初始状态 \(1\),每次可以在状态 \(u\) 后面扩展一个 \(0\) or \(1\) 构成新状态 \(v\),然后判断一下扩展的数 \(v \bmod n\) 是否为 \(0\),如果为 \(0\) 那么就输出就好了。

使用记忆化搜索,用数组 mod[i] 储存 \(m \bmod n=i\) 的最小 \(m\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e6+7;
ll n;
struct node
{
	bool s[100];
	int len;
}mod[N];  //mod[i]:m%n余数为i的最小m 
queue<int> q;  //队列储存的是m%n 
bool vis[N];
inline bool print(int k)
{
	for(int i=1;i<=mod[k].len;i++) cout<<mod[k].s[i];
	return 1;
}
inline bool bfs()
{
	q.push(1);
	mod[1].len=1,mod[1].s[1]=1;  //1入队 
	vis[1]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		
//		cerr<<u<<":";print(u);cout<<'\n';
		
		int v=(u*10)%n;  //在u后面加个0
		if(!vis[v])
		{
			vis[v]=1;
			mod[v]=mod[u];
			mod[v].s[++mod[v].len]=0;  //末尾加0
			if(v==0) return print(0);
			q.push(v);
		}
		
		v=(u*10+1)%n;  //在u后面加个1
		if(!vis[v])
		{
			vis[v]=1;
			mod[v]=mod[u];
			mod[v].s[++mod[v].len]=1;
			if(v==0) return print(0);
			q.push(v);
		}
	}
	return 0;
}
int main()
{
//	freopen("T1.in","r",stdin);
//	freopen("T1.out","w",stdout);
	cin>>n;
	if(bfs()==false) cout<<"-1";
	return 0;
}

B

定义两个 01 串的相似值为:如果两个 01 串在第 \(i\) 位是一样的,那么他们的相似值要加上 \(w_i\)。给定 \(m\) 个长度为 \(n\) 的 01 串,\(q\) 次询问,每次询问给定一个长度为 \(n\) 的 01 串 \(S\) 和一个整数 \(k\),问在这 \(m\) 个串中与串 \(S\) 相似值不超过 \(k\) 的个数。\(n \le 15,w_i,k \le 30,m,q \le 5 \times 10^5\)

样例输入

2 4 5
4 2
01
01
10
11
00 2
00 4
11 2
11 4
11 6

样例输出

2
4
2
3
4

看到“01 串”和“\(n \le 15\)”可以考虑状态压缩 DP。原题面“相似值”可以转换成“将字符串修改若干个字符,如果不修改那么就要付出 \(w_i\) 的代价”于是可以转化成背包问题。设 dp[range][state][cost] 表示只操作 [range,n] 位,将字符串 state 使用不超过 cost 的代价可以变成多少个目标串(目标串就是题目给定的 \(m\) 歌长度为 \(n\) 的 01 串)

初始时,任意字符串只变 \([n+1,n]=\varnothing\) 位显然只能变成自己,故 \(dp(n+1)(state=0 \sim 2^n-1)(0 \sim W)\) 都为输入的 \(m\) 个串中 \(state\) 的个数。(根据题目 \(w_i \le 30\) 可设 \(W=30\)

DP 转移就直接按照背包的思路转移就好了。预处理时间复杂度 \(O(2^n nW)\),单次询问 \(O(1)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=15;
constexpr int M=5e5+7;
constexpr int S=1<<N;
constexpr int W=30;
int n,m,Q;
int cnt[S+7],w[N+2];
int dp[N+2][S+2][W+2];
inline int read()
{
	int w=0,t=0;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
	{
		if(c=='1') w|=(1<<t);
		c=getchar();
		t++;
	}
	return w;
}
inline int solve()
{
	int q,k;
	q=read();cin>>k;
	return dp[1][q][k];
}
int main()
{
//	freopen("T2.in","r",stdin);
//	freopen("T2.out","w",stdout);
	cin>>n>>m>>Q;
	for(int i=1;i<=n;i++) cin>>w[i];
	for(int i=1;i<=m;i++)
	{
		int tmp=read();
		cnt[tmp]++;
	}
	
	for(int state=0;state<(1<<n);state++)
	for(int cost=0;cost<=W;cost++) dp[n+1][state][cost]=cnt[state];
	
	for(int range=n;range>=1;range--)
	{
		for(int state=0;state<(1<<n);state++)
		{
			for(int cost=0;cost<=W;cost++)
			{
				dp[range][state][cost]=dp[range+1][state^(1<<(range-1))][cost];  //变 
				if(cost>=w[range]) dp[range][state][cost]+=dp[range+1][state][cost-w[range]]; //不变 
//				cerr<<range+1<<' '<<state<<' '<<cost<<' '<<dp[range+1][state][cost]<<'\n';
			}
		}
	}
	while(Q--) cout<<solve()<<'\n';
	return 0;
}
/*
背包问题? 
相似值w[i]可转化为代价,相同则要花费代价,不相同不用花费代价 
dp[range][state][cost]表示只操作[range,n]位,将字符串state使用不超过cost的代价可以转换成多少个目标串 
*/

C

给出 \(t\) 个数字,其中包括一个 \(0\),求是否存在一个 \(n \times m\) 的矩阵,满足 \(n \times m=t\),在其中选择一个特殊格子 \((x,y)\),然后将这 \(t\) 个数字填到矩阵中,满足每个格子上的数字就是到特殊格子的曼哈顿距离。如果有多种方案,输出任意一种 \(n,m,x,y\),如果无解则输出 \(-1\)\(t \le 10^6\)
样例输入

20
1 0 2 3 5 3 2 1 3 2 3 1 4 2 1 4 2 3 2 4

样例输出

4 5
2 2

观察到:

  1. 在矩阵无限大的情况下,除 \(0\) 以外,每一个数字 \(x\) 应该出现了 \(4x\) 次,如果出现次数不为 \(4x\) 则可以称数字 \(x\) 为“异常数字”。从 \(1\) 开始找到第一个异常数字,发现总有一种构造方案使得这个异常数字就是特殊格子 \((x,y)\)\(x\) 坐标。

  2. 存在一种构造方案,使得给出的 \(t\) 个数字中最大的数字 \(d\) 就是特殊格子到 \((n,m)\) 的曼哈顿距离。并且以上两种方案并不冲突。

由曼哈顿距离公式 \(dis=|x_1-x_2|+|y_1-y_2|\)\(d=n+m-x-y\)。目前已知 \(d,x\),可以在 \(1 \sim \sqrt t\) 的范围下枚举一下 \(n\)\(m\),然后根据等式 \(d=n+m-x-y\) 算出 \(y\),然后在 \(O(nm)\) 的时间内检查一下。总时间复杂度 \(O(\sqrt t nm)\)。注意一下只枚举 \(n\) 是会被卡一个点的,所以要在枚举完 \(n\) 后再把 \(m\) 枚举一遍。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e6+7;
int t,a[N];
int cnt[N];
int cnt2[N];
int n,m,x=0,y,d;
inline void check()
{
	if(abs(n-x)+abs(m-y)!=d) return;
	for(int i=0;i<=n+m;i++) cnt2[i]=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++) cnt2[abs(i-x)+abs(j-y)]++;
	
	for(int i=0;i<=n+m;i++) if(cnt[i]!=cnt2[i]) return;
	cout<<n<<" "<<m<<"\n"<<x<<" "<<y;
	exit(0);
}
int main()
{
//	freopen("T3.in","r",stdin);
//	freopen("T3.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>t;
	int mx=-1;
	for(int i=1;i<=t;i++)
	{
		cin>>a[i];
		mx=max(mx,a[i]);
		cnt[a[i]]++;
	}
	if(t==1)
	{
		if(a[1]==0) cout<<"1 1\n1 1";
		else cout<<"-1";
		return 0;
	}
	d=*max_element(a+1,a+t+1);
	for(int i=1;i<=mx;i++) if(cnt[i]!=4*i){x=i;break;}
	
	for(n=1;n*n<=t;n++)
	{
		if(t%n==0)
		{
			m=t/n;
			y=n+m-x-d;
			check();
		}
	}
//	
	n=m=0;
	
	for(m=1;m*m<=t;m++)
	{
		if(t%m==0)
		{
			n=t/m;
			y=n+m-x-d;
			check();
		}
	}
	cout<<"-1";
	return 0;
}
/*
2   1   2   3   4
1   0   1   2   3
2   1   2   3   4
3   2   3   4   5


0所在的格子:(2,2)         为P(x,y) 
5(最大值)所在的格子:(4,5) 为Q(n,m)
d=最大值=dist(P,Q)=n+m-x-y   => y=n+m-x-d
如果这个矩形无限大,各个数字出现的次数:0-1 1-4 2-8 3-12 (n)-(4n) 
从1开始第一个异常数字(出现次数不为4n)就是x 
枚举n,m然后算出y然后检验 
*/

D

给你 \(n\) 个(非负整)数 \(a_1,a_2,\cdots,a_n\),对于每一个数 \(a_i\),判断是否存在一个 \(j\) 使得 \(a_i \& a_j=0\)。(其中 \(\&\) 表示按位与)\(a_i,n \le 10^6\)
样例输入

6
1 2 3 5 7 9

样例输出

1 1 0 1 0 1

看到位运算题目首先按位考虑。我们可以发现,对于每一个非负整数 \(x\),都有 \(x \& \operatorname{not} x=0\)\(\operatorname{not}\) 表示按位取反),因此可以开一个标记数组 vis\(\operatorname{not} a_i\) 标记一遍,然后再从 \(i=2^K \sim 0\) 扫一遍,如果 \(i\) 被标记过那么就考虑 \(i\) 的每一位,如果当前位是 \(1\) 那就把这一位变成 \(0\) 再标记上(因为 \(0 \& 0=0\))。最后输出 vis[a[i]] 即可。

输入输出量有点大,可能需要优化 I/O 速度卡一下常 但是似乎不卡常也能过?

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N=1e6+7;
constexpr int K=20;
constexpr int M=(1<<K)-1;
int n,a[N];
bool vis[3*M+5];
int main()
{
//	freopen("T4.in","r",stdin);
//	freopen("T4.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		vis[a[i]^M]=1;  //forall 非负整数x,x&~x=0 
	}
	for(int i=M;i>=0;i--)
	{
		if(vis[i])
		{
			for(int j=K+1;j>=0;j--)
			{
				if(i&(1<<j)) vis[i^(1<<j)]=1;
			}
		}
	}
	for(int i=1;i<=n;i++) cout<<vis[a[i]]<<' ';
	return 0;
}
posted @ 2025-08-06 19:02  wwwidk1234  阅读(37)  评论(0)    收藏  举报