Day·6
看到题目难受的虚伪了
Problem·1
题面
给出一张只有1和0的图,可以交换任意两列组成一个以一为元素的矩阵,求最大形成的矩阵中有几个1
思路
第一眼,觉得应该是DP于是果断放弃,关键是DP做的实在是太少了………………难受死了
题解
部分分:考虑矩阵的上下边界,求第i-j行全为1的列的数量,暴力判断N^3按顺序枚举N^2
仅枚举矩阵的下边界,递推维护每个数字向上连续1的个数。
有意义的上边界仅有M个(向上连续1终止的位置)。
维护这些上边界:下边界i-1到i时,上边界要么+1,要么重置为i。+1的上边界相对大小不改变,稍加维护即可。复杂度O(N*M)。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
char z[1530];
int a[1530],t[1530];
int p[1530];
int ans=0;
int main()
{
freopen("logs.in","r",stdin);
freopen("logs.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)t[i]=i;
for(int i=1;i<=n;i++){
scanf("%s",&z[1]);
for(int j=1;j<=m;j++)p[j]=0;
for(int j=1;j<=m;j++){
if(z[j]=='1'){
}
else
p[t[j]]=1;
}
for(int j=1;j<=m;j++)p[j]+=p[j-1];
int tt=m-p[m]+1;
for(int j=1;j<=m;j++){
if(z[j]=='1'){
t[j]-=p[t[j]];
a[j]++;
ans=max(ans,a[j]*t[j]);
}
else
t[j]=tt++,a[j]=0;
}
}
cout<<ans<<endl;
fclose(stdin);
fclose(stdout);
return 0;
}
Problem·2
题面
给出两个数,m和p,求以p为最小质因子的第m个数,如果大于10^18输出0,保证p为质数
思路
拿到题想错了,想成了最小的数也得满足p^m,但是没有考虑到质数相乘会小于p^m的情况,比个例子,m=3,p=3,我原以为ans满足>=3^3实际上3*5也满足了这个条件而不是单纯的p的次幂,于是难过,想错了就不想做了,byebye第二题,我要虚伪了。
题解
对大P,小P分别求解。
P比较大时,P在10^9以内倍数不多,求一个质数表,p乘以第n-1个大于等于P的质数 复杂度10^9/P
P比较小时,二分答案,对小于P的质数进行容斥。设t=小于p质数个数。复杂度 log(10^9)*(2^t)*t
代码
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef long long llint;
const int C = 50;
const int MAX = 1000000000;
llint f(int mid, int p) {
vector<int> prime, v(p, 0);
for (int i = 2; i < p; ++i)
if (!v[i]) {
prime.push_back(i);
for (int j = i + i; j < p; j += i)
v[j] = 1;
}
llint ret = mid;
for (int mask = 1; mask < (1 << (int) prime.size()); ++mask) {
llint d = 1, sgn = -1;
for (int i = 0; i < prime.size(); ++i)
if (mask & (1 << i)) {
d *= prime[i];
if (d > mid) break;
sgn *= -1;
}
ret -= sgn * mid / d;
}
return ret;
}
void solve_small(int n, int p) {
int ans = 0;
int lo = 1;
int hi = MAX / p;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
long long k = f(mid, p);
if (k < n)
lo = mid + 1;
else
hi = mid;
}
if (f(lo, p) == n)
ans = lo * p;
printf("%d\n", ans);
}
void solve_large(int n, int p) {
int ans = 0;
if (n == 1)
ans = p;
int len = MAX / p + 1;
char* v = new char[len];
memset(v, 0, len);
for (int i = 2, k = 1; i < len; ++i)
if (!v[i]) {
if (i < p)
for (llint j = (llint) i * i; j < len; j += i)
v[j] = 1;
else {
++k;
if (k == n)
ans = i * p;
}
}
delete[] v;
printf("%d\n", ans);
}
int main(void) {
freopen("broj.in","r",stdin);
freopen("broj.out","w",stdout);
int n, p;
scanf("%d %d", &n, &p);
if (p < C)
solve_small(n, p);
else
solve_large(n, p);
fclose(stdin);
fclose(stdout);
return 0;
}
Problem·3
题面
操场上有N个人,你要帮助他们组成小组。每个人有一个愿望值Ai,表示自己所在小组不少Ai人。求在满足最多人愿望的情况下,最多能划分出几个小组。在满足最多人愿望且划分小组个数最多的情况下,最小化最大小组内的人数。求小组数和最大小组的人数。
思路
虚伪之力………………
题解
对每个人的需求A_i从大到小排序。
初步思考:考虑满足第一个人的需求,暂时选择前A_1个人组成一个小组。理由:之后的人的需求弱于之前的人,因此前A_1个人组成一组一定合法。把这A_1个人跟其余人交换,不会更优。
考虑第A_1+1个人,这个人要么加入前A_1个人的小组,要么自成一组且成为小组最强需求。。。。。
第一问解法:
设f[i]表示前i个人构成的最多小组数量。
加入:若f[i-1]>0即已有一些小组,那么第i个人可以加入之前的小组。f[i]可以从f[i-1]转移。
构成:k-1+A_k==i即k~i个人正好构成以A_k为上限的小组。
若枚举复杂度较大。
考虑到k-1+A_k只有n个值,可以预先更新。即从小到大计算f,计算f[k-1]时更新f[i]。
第二问解法:
同上考虑,设g[i]表示前i个人构成最多小组时,最小可能取到的最大小组人数。
若i加入之前小组,则g[i]从g[i-1]转移。(注意考虑是否每组都达到最大人数)
若构成小组,g[i]从g[k-1]转移。K可能有多个,但总数不超过n。
总复杂度O(n)
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int a[1000005];
int f[1000005],g[1000005];
int main() {
freopen("team.in","r",stdin);
freopen("team.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(&a[1],&a[n+1]);
for(int i=1;i<=n;i++)f[i]=-n*2,g[i]=n*2;
for(int i=n;i>=1;i--){
int x=max(0,i-a[i]+1);
if(f[i+1]>=0){
if(f[x]<f[i+1]+1){
f[x]=f[i+1]+1;
g[x]=max(g[i+1],a[i]);
}
else
if(f[x]==f[i+1]+1){
g[x]=min(g[x],max(g[i+1],a[i]));
}
}
if(f[i+1]>0){
if(f[i]<f[i+1]){
f[i]=f[i+1];
g[i]=g[i+1]+(n-i==g[i+1]*f[i+1]);
}
else
if(f[i]==f[i+1]){
g[i]=min(g[i],g[i+1]+(n-i==g[i+1]*f[i+1]));
}
}
}
printf("%d\n%d\n",f[1],g[1]);
fclose(stdin);
fclose(stdout);
return 0;
}
上课的内容
讲了DP,嗯,很重要………………我觉得关键还是多练吧
给个几道模板,《玩具装箱(斜率优化)》,