EduRound 80
A
题意
有一项任务,要求在n天内完成,不优化的话需要d天,如果用 x 天来优化则可以变成 d/(x+1) (向下取整)天。即总共花 x+d/(x+1) 天,求是否可以按要求完成。
思路
总时间很明显是个凹函数,考虑三分,但是由于存在向下取整,所以不是严格凹函数,所以不能三分。但是可以直接求导,求导一下得到最优的x为sqrt(4d)/2-1。
代码
#include<bits/stdc++.h>
using namespace std;
int n,d;
int f(int x)
{
return x+d/(x+1)+(d%(x+1)>0);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&d);
int x=int(sqrt(4.0*d)/2.0-1);
printf(f(x)<=n?"YES
":"NO
");
}
}
B
题意
给出A,B寻找有多少对a,b, a∈[1,A],b∈[1,B] 使得 ab+a+b=a拼接b 。拼接即两数连接在一起得到新数,如 6拼接9得到69
思路
打表找规律,发现b是9,99,998,......,a 任选,所以计算一下 [1,B] 有多少 9... 这种数,再乘 A 就是答案。
代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
long long a,b,k=0,cur=9;
scanf("%I64d%I64d",&a,&b);
while(cur<=b)
{
k++;
cur=cur*10+9;
}
printf("%I64d
",a*k);
}
}
C
题意
给出n,m,求有多少对数组a,b满足:
- a,b长度均为m
- a,b中所有元素均∈[1,n](可重复)
- 所有对应位置元素有ai<=bi
- a保证单调不降
- b保证单调不增
思路
分开考虑比较复杂,所以考虑a,b合起来,将a正序排列,b倒序排列接在a后面,则可以得到一个c,使得c满足长度为2m,且单调不降。则问题转化为 [1,n] 中选择 2m 个可重复的数使得构成一个单调不降序列。可以用两种方法解决
-
DP:状态定义为dp[i][j]表示c中第i位为j的选法个数。因为保持单调不降,所以转移就由前一位小于等于j的状态转移而来。
-
组合数学:假设 [1,n] 第 i 个数选择 xi 次,则问题转化为方程
[sum_{i=1}^nx_i=2m ]用隔板法解决。因为可选多次,可不选,所以增加n个,答案就是
[C_{2m+n-1}^{n-1} ]
代码
DP
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e3+3;
const int mod=1e9+7;
int dp[25][MAX];
int main()
{
int n,m,res=0;
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
dp[1][i]=1;
for(int i=2; i<=2*m; i++)
for(int j=0; j<n; j++)
for(int k=0; k<=j; k++)
dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
for(int i=0;i<n;i++)
res=(res+dp[2*m][i])%mod;
printf("%d
",(res+mod)%mod);
}
排列组合
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll qpow(ll x,ll k)
{
ll res=1;
while(k)
{
if(k&1)
res=res*x%mod;
k>>=1;
x=x*x%mod;
}
return res;
}
ll inv(ll x){return qpow(x,mod-2);}
ll jc(ll x)
{
ll res=1;
while(x){res=res*x%mod;x--;}
return res;
}
int main()
{
ll n,m,a,b;
scanf("%I64d%I64d",&n,&m);
a=2*m+n-1;
b=n-1;
n=jc(a);
m=jc(b)*jc(a-b)%mod;
printf("%I64d
",n*inv(m)%mod);
}
D
题意
给出n个数组,长度均为m,可以选两个数组(可以相同),然后对应位置取max得到一个新数组,求如何选择使得最后得到的新数组中的最小值最大。
思路
最小值最大很容易想到二分答案,所以问题就是如何快速判断一个解是否可行。因为m很小,所以容易想到状态压缩。假设当前判断答案x。对于任意一个数组a,如果a[i]>=x,则记此位为1,否则记为0,则所以数组都可以压缩成最多255的数,所以状态总共有255种,可以O(n^2)的判断两个数组是否可行,可行即二者按位或得到全为1
代码
#include<bits/stdc++.h>
using namespace std;
const int MAX=3e5+5;
int n,m,ra,rb,a[MAX][10];
int mp[(1<<8)];
bool check(int x)
{
memset(mp,0,sizeof mp);
for(int i=0; i<n; i++)
{
int cur=0;
for(int j=0; j<m; j++)
cur|=(a[i][j]>=x)<<(m-j-1);
mp[cur]=i+1;
}
int tot=(1<<m);
for(int i=0; i<tot; i++)
for(int j=0; j<tot; j++)
if(mp[i]&&mp[j]&&((i|j)==tot-1))
{
ra=mp[i],rb=mp[j];
return 1;
}
return 0;
}
int main()
{
int L=0,R=0,res;
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
scanf("%d",&a[i][j]),R=max(R,a[i][j]);
while(L<=R)
{
int mid=(L+R)>>1;
if(check(mid))
{
res=mid;
L=mid+1;
}
else
R=mid-1;
}
if(ra>rb)swap(ra,rb);
printf("%d %d
",ra,rb);
}
E
题意
给一个长度为n的排列,初始为1-n,m个操作,每次选一个x,然后把x从x原来所在位置p[x]提到第一位,1-p[x]-1的数依次往后推,求1-n在操作过程中位置能达到的最大值和最小值
思路
将题目中操转化为在排列前面预留m个位置,然后每次操作将p[x]置零,然后将最左边的位置改为x,这样就避免了往后推的操作。然后min就是1或者最初的位置,max就是位于前面的数的数量。如果x没有被提到第一位,则min不会变化,max只会增加,所以用树状数组维护,改变了的x在操作过程中更新,没有改变的x在所有操作结束后更新。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAX=3e5+5;
int n,m,c[MAX<<1],p[MAX],minn[MAX],maxx[MAX];
void add(int x,int k)
{
for(;x<=n+m;x+=x&-x)
c[x]+=k;
}
int query(int x)
{
int sum=0;
for(;x;x-=x&-x)
sum+=c[x];
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
p[i]=i+m;
minn[i]=i;
maxx[i]=i;
add(p[i],1);
}
for(int i=0;i<m;i++)
{
int x;
scanf("%d",&x);
maxx[x]=max(maxx[x],query(p[x]));
minn[x]=1;
add(p[x],-1);
p[x]=m-i;
add(p[x],1);
}
for(int i=1;i<=n;i++)
{
maxx[i]=max(maxx[i],query(p[i]));
printf("%d %d
",minn[i],maxx[i]);
}
}