A.Intelligent Warehouse
题意:在给定长度为n的序列中,找到一个答案序列,使得答案序列中任意两个数相互为倍数,输出最大长度。用dp[i]表示i所能构成的最大序列长度,所以只要对i的整数倍进行更新,取最大值即为答案。(对整数倍进行更新,可以简化为对i的素数倍进行更新,因为合数可以由若干个素数相乘所得,从而简化了时间复杂度)
#include <iostream>
using namespace std;
const int N=1e7;
int primes[N],cnt;
bool st[N];
int num[N],dp[N];
void get_primes()
{
for(int i=2;i<=N;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]*i<=N;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
int main()
{
int n;
cin>>n;
get_primes();
for(int i=0;i<n;i++)
{
int x;
cin>>x;
num[x]++; //用桶装入数字
}
int ans=0;
for(int i=1;i<=N;i++)
{
dp[i]+=num[i];
ans=max(ans,dp[i]);
for(int j=0;j<cnt&&primes[j]*i<=N;j++) //从dp[j]往前递推
dp[i*primes[j]]=max(dp[i],dp[i*primes[j]]);
}
cout<<ans<<endl;
return 0;
}
F.Design Problemset
题意:给定k个物品,每个物品有ai个,对k个物品进行分组,使得每个组的物品数属于[L,R],同时每个物品每组可以分出的个数属于[li,ri],求最多能分为多少组,我们对组数(mid)二分求答案,可以满足的条件就是就是li*mid<=ai,即对于每组每个物品最少可以分li个,分为mid组后所需的物品数应该小于物品原有个数,同时还有物品有剩余,将其累加为sum,得到判断条件(理解不够透彻),最后得出答案即可。tips:数据范围过大,需用unsigned long long 进行保存。
if(minn+sum/mid>=L&&minn<=R) return true
return false;
#include <iostream>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10;
struct node
{
ll l,r,k;
}a[N];
ll n,L,R;
bool check(ull mid)
{
if(mid==0) return true;
ull sum=0,minn=0;
for(int i=1;i<=n;i++)
{
if(a[i].l*mid>a[i].k) return false; //无法分出mid组
minn+=a[i].l;
sum+=min((a[i].r-a[i].l)*mid,a[i].k-mid*a[i].l);
}
if(minn+sum/mid>=L&&minn<=R) return true;
return false;
}
int main()
{
cin>>n>>L>>R;
for(int i=1;i<=n;i++) cin>>a[i].k;
for(int i=1;i<=n;i++) cin>>a[i].l>>a[i].r;
ll s=0,e=1e18;
while(s<=e)
{
ull mid=(s+e)/2;
if(check(mid)) s=mid+1;
else e=mid-1;
}
cout<<e;
return 0;
}
I.Walking Machine
题意:每个方块只能进行对应的移动操作,求所有可通过移动出图的点的个数。正着想并不好做,此题可以逆思维,对所有在图外的点进行bfs,看能到达图内多少个点,记录答案即可。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int,int> PII;
const int N=1010;
char map[N][N];
int n,m,ans;
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
queue<PII> q;
void bfs()
{
while(q.size())
{
int x=q.front().first,y=q.front().second;
q.pop();
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<1||a>n||b<1||b>m) continue;
if(i==0&&map[a][b]=='A') q.push({a,b}),ans++;
else
if(i==1&&map[a][b]=='D') q.push({a,b}),ans++;
else
if(i==2&&map[a][b]=='S') q.push({a,b}),ans++;
else
if(i==3&&map[a][b]=='W') q.push({a,b}),ans++;
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>map[i][j];
for(int i=1;i<=m;i++) q.push({0,i}),q.push({n+1,i});
for(int i=1;i<=n;i++) q.push({i,-0}),q.push({i,m+1}); //记录图外的点
bfs();
cout<<ans<<endl;
return 0;
}
j.Matrix Subtraction
题意:给定一个宽n高m数字矩阵,求用宽a高b的子矩阵进行操作,每次可使子矩阵大小内的所有数字减1,求在有限次内是否可以使数字矩阵全部减为0。若对数(i,j)进行考虑,想对该数进行操作,则需要用左上角为(i,j)右下角为(i+a-1,j+b-1)的子矩阵,那么对每个点都进行如此操作,直到留下子矩阵大小的数,进行判断后得出答案。直接遍历时间复杂度过高,所以用二维差分,前缀和进行优化。
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e3+10;
ll f[N][N];
int n,m,a,b;
void ins(int x,int y,ll c,int a,int b) //二维差分操作
{
int dx=x+a;
int dy=y+b;
f[x][y]-=c;
f[x][dy]+=c;
f[dx][y]+=c;
f[dx][dy]-=c;
}
int main()
{
int t;
cin>>t;
while(t--)
{
memset(f,0,sizeof f);
cin>>n>>m>>a>>b;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x;
cin>>x;
ins(i,j,-x,1,1); //负号为加
}
int flag=1;
for(int i=1;flag&&i<=n;i++)
for(int j=1;flag&&j<=m;j++)
{
f[i][j]+=f[i][j-1]+f[i-1][j]-f[i-1][j-1];
if(f[i][j]==0) continue;
if(f[i][j]<0) flag=0;
if(f[i][j]>0)
{
if(i+a-1>n||j+b-1>m) flag=0;
else ins(i,j,f[i][j],a,b);
}
}
if(flag) puts("^_^");
else puts("QAQ");
}
return 0;
}