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

浙公网安备 33010602011771号