【1th】 小米邀请赛
传送门
A.Intelligent Warehous
题意
给定\(n\)个数,从这\(n\)个数中选出一些数来,这些数构成的集合中任选两个数其中的一个都能被另一个整除
求最大的集合中数字的个数
数据范围
\(1\leq n\leq 2\times 10^{5}\)
题解
数论动态规划,倍数法,\(dp[i]\)表示数字\(i\)之前所有数字所在集合的最大数量
- 不能通过倍数进行累加,如果是累加的话\(3、4\)两个数不存在其中一个是另一个的约数,同样会加到\(12\)上
- 只需要将当前数的倍数更新为最大值即可,后面再加上当前数的个数
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
const int N=1e7+10;
int n;
bool st[N];
int dp[N],cnt[N];
int main(){
scanf("%d",&n);
rep(i,1,n+1){
int x;scanf("%d",&x);
cnt[x]++;
st[x]=1;
}
int ans=0;
rep(i,1,N){
if(!st[i]) continue;
dp[i]+=cnt[i];
ans=max(ans,dp[i]);
for(int j=i+i;j<N;j+=i)
if(dp[i] >= dp[j])
dp[j]=dp[i];
}
cout<<ans<<endl;
}
C. Smart Browser
题意
统计一个字符串中\(w\)中的$/$字符的数量
数据范围
\(1\leq |s| \leq 10^{5}\)
题解
只有\(w\)才会产生,一段连续长度为\(len\)的\(w\)字符串中的数量为\(2\times len -1\),只需要累计连续的长度计算即可,计算的时候需要判断是否为\(0\),否则\(-1\)后影响答案
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
int main(){
string s;
cin>>s;
int len=s.size();
int leng=0;
int ans=0;
rep(i,0,len){
if(s[i]=='w') ++leng;
else{
if(leng){
ans+=2*leng-1;
leng=0;
}
}
}
if(leng) ans+=2*leng-1;
cout<<ans<<endl;
}
I. Walking Machine
题意
给定一个大小为\(n\times m\)的迷宫,每个格点上有一个字符表示这个格子可以移动的方向
- \(W\):向左移动,\((x,y)\rightarrow (x-1,y)\)
- \(A\):向上移动,\((x,y)\rightarrow (x,y-1)\)
- \(S\):向右移动,\((x,y)\rightarrow (x+1,y)\)
- \(D\):向下移动,\((x,y)\rightarrow (x,y+1)\)
求出在所有格点出发能够走出迷宫的格点数量
数据范围
\(1\leq n,m\leq 1000\)
题解
对于每条边,建立反向边,一个源点\(0\)表示当前图的界外,如果边出界全部连到点\(0\)处,
然后从表示界外的源点\(0\)开始\(dfs\)遍历图即可,每个点只需要被遍历一次,累计点数即可
- 二维图的格点\((x,y)\)压缩成一维后值为\(x\times m + y\),\(n\)表示行数,\(m\)表示列数
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
const int N=1010,M=N*N;
int n,m;
char s[N][N];
bool st[M];
struct node {
int to,ne;
}e[M];
int h[M],id;
int ans=0;
int get(int x,int y){
if(x<1 || x>n || y<1 || y>m) return 0;
return (x-1)*m+y;
}
void add(int u,int v){
e[++id].to=v;
e[id].ne=h[u];
h[u]=id;
}
void dfs(int u){
for(int i=h[u];i;i=e[i].ne){
int to=e[i].to;
if(st[to]) continue;
st[to]=1;ans++;
dfs(to);
}
}
int main(){
scanf("%d%d",&n,&m);
rep(i,1,n+1) scanf("%s",s[i]+1);
rep(i,1,n+1) rep(j,1,m+1){
if(s[i][j]=='W') add(get(i-1,j),get(i,j));
else if(s[i][j]=='A') add(get(i,j-1),get(i,j));
else if(s[i][j]=='S') add(get(i+1,j),get(i,j));
else if(s[i][j]=='D') add(get(i,j+1),get(i,j));
}
st[0]=1;
dfs(0);
cout<<ans<<endl;
}
J. Matrix Subtraction
题意
给定一个\(n\times m\)大小的矩阵,给定一个\(a\times b\)的子矩阵范围,每次可以将大小为\(a\times b\)的任意子矩阵中所有元素值\(-1\)问当前的\(n\times m\)的矩阵能否经过操作变为全\(0\)的矩阵
数据范围
\(1\leq n,m\leq 1000\)
题解
对于格点\(M_{1,1}\),只能被\((1,1)\sim (a,b)\)的子矩阵处理,所以\((1,1)\sim (a,b)\)的选择次数是确定的,同理可以求出\((1,2)\sim (a,b+1)\)及其它所有子矩阵被选择的次数
\(C_{i,j}\)为\((i,j)\sim (i+a-1,j+b-1)\)被选择的次数,可以得到:
运用前缀和+二维差分可以\(O(1)\)计算出
- 只需要判断每个\(C_{i,j}\)是否\(\geq 0\)以及所有\(M_{i,j}\)能否恰好变为\(0\)
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
const int N=1010;
int n,m,a,b;
int g[N][N];
int c[N][N];
void solve(){
scanf("%d%d%d%d",&n,&m,&a,&b);
rep(i,1,n+1) rep(j,1,m+1) scanf("%d",&g[i][j]);
rep(i,1,n+1) rep(j,1,m+1) c[i][j]=0;
rep(i,1,n+1) rep(j,1,m+1){
c[i][j]=c[i][j]+c[i-1][j]+c[i][j-1]-c[i-1][j-1];
g[i][j]-=c[i][j];
if(g[i][j]<0) {
return puts("QAQ"),void(0);
}
if (i+a-1>n||j+b-1>m){
if (g[i][j]) return puts("QAQ"),void(0);
}
else{
c[i][j]+=g[i][j];
c[i+a][j]-=g[i][j];
c[i][j+b]-=g[i][j];
c[i+a][j+b]+=g[i][j];
}
}
puts("^_^");
}
int main(){
int _;scanf("%d",&_);
while(_--) solve();
}