简单数学
Luogu P1372:1-n中找出k个数使得这k个数的gcd最大并求这个值。
要使x与另一个数的gcd尽量大,另一个数最好是x的倍数。即,答案序列应为x,x*2,x*3...x*k。又因为x*k不大于n,那么答案就是n/k。
Luogu P1338:求1-n的逆序数为m的字典序最小的排列。
若排列中已经确定了一些数字,那么对于剩下的i个位置,能够产生的最大逆序数是i*(i-1)/2。并且一个排列的逆序数越多,字典序就越大,那么如果m不比当前的最多逆序数大,就可以将最小数直接放在字典序最小的位置上也就是最前面的位置。否则,考虑最小数放置的位置,在长度为i的排列中一个数的位置改变最多可以产生i-1个逆序,要使字典序最小,就必须让剩下数字产生的逆序数最小,即此时将最小数放到最后一个位置上。
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define maxn 1000050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
ll n,m;
int a[maxn];
int main()
{
scanf("%I64d%I64d",&n,&m);
int l=1,r=n;
for(int i=1;i<=n;i++)
{
ll tmp=(ll)(n-i)*(n-i-1)/2;
if(m<=tmp)a[l++]=i;
else
{
a[r--]=i;
m-=(r-l+1);
}
}
for(int i=1;i<=n;i++)
printf(i==n?"%d\n":"%d ",a[i]);
return 0;
}
Luogu P2158:从n*n方阵的左下角看去,问能看到多少个人。
有三个特殊点(1,0),(1,1),(0,1),其余的点要想被看到,可知坐标(x,y)需满足x与y互质。
即:ans=3+2*phi(i) i:2->n-1
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define maxn 40050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
int n;
int phi[maxn];
void init()
{
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!phi[i])
{
for(int j=i;j<=n;j+=i)
{
if(!phi[j])phi[j]=j;
phi[j]=phi[j]*(i-1)/i;
}
}
}
}
int main()
{
scanf("%d",&n);
if(n==1){printf("0\n");return 0;}
init();
int ans=0;
for(int i=2;i<n;i++)
ans+=phi[i];
printf("%d\n",2*ans+3);
return 0;
}
Luogu P1582:n堆初始为1,每次将两堆相同的合并,现在需要合并到不超过k堆,问最少需要增加多少堆。
n堆,最终合并的情况是和n的各位二进制相对应的。那么就每次加上lowbit(n),直至二进制中1的个数不超过k为止。
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define maxn 40050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
int n,k;
int num(int x)
{
int tmp=x;
int ans=0;
while(tmp)
{
ans+=(tmp%2);
tmp/=2;
}
return ans;
}
int lowbit(int x){return x&-x;}
int main()
{
scanf("%d%d",&n,&k);
int ans=0;
while(1)
{
int cnt=num(n);
if(cnt<=k)break;
ans+=lowbit(n);
n+=lowbit(n);
}
printf("%d\n",ans);
return 0;
}
二维dp
Luogu1387:给出一个n*m的01矩阵,问最大的不含0的正方形的边长是多少。
在将矩阵扫一遍的过程中,在左方、上方、左上方中取最小值即可。
状态转移方程dp[i][j]=min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1,其中要求a[i][j]为1。
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define maxn 105
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
int n,m;
int dp[maxn][maxn];
int a[maxn][maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j]==0)
continue;
int tmp=min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j]));
dp[i][j]=tmp+1;
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
ans=max(ans,dp[i][j]);
}
printf("%d\n",ans);
return 0;
}
Luogu1736:给定一个01矩阵,问最大的仅对角线为1的正方形的边长是多少。
和上一个很像,只不过需要一个预处理。
S1[i][j]表示最多从(i,j)向左/右延伸多少个格子,使得这些格子都为0,S2表示向上的相同含义。进行预处理之后,状态转移方程为dp[i][j]=min(dp[i-1][j-1],s1[i][j-1],s2[i-1][j])+1。
对角线有可能是向左或者向右的,因此需要正着扫一遍再倒着扫一遍。
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define maxn 2505
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
int n,m;
int a[maxn][maxn];
int dp[maxn][maxn],s[maxn][maxn],t[maxn][maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
}
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(!a[i][j])
{
s[i][j]=s[i][j-1]+1;
t[i][j]=t[i-1][j]+1;
}
if(a[i][j])
dp[i][j]=min(dp[i-1][j-1],min(s[i][j-1],t[i-1][j]))+1;
ans=max(ans,dp[i][j]);
}
}
memset(dp,0,sizeof(dp));
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++)
{
for(int j=m;j>=1;j--)
{
if(!a[i][j])
s[i][j]=s[i][j+1]+1;
if(a[i][j])
dp[i][j]=min(dp[i-1][j+1],min(s[i][j+1],t[i-1][j]))+1;
ans=max(ans,dp[i][j]);
}
}
printf("%d\n",ans);
return 0;
}