Educational Codeforces Round 80 (Rated for Div. 2)
C. Two Arrays
题意
题目可以转换成求一个长度为2m的不递减数组,元素取值为1-n;求得方案数
思想
可以考虑将a,b数组连起来形成一个b[1],b[2],…,b[m],a[m],a[m-1],…,a[1]的数组,长度为2m且非严格递减,取值都在[1,n]之间(与原问题等价),就相当于有n个盒子排成一排,由他们的顺序决定所取的数字(即n个不同的盒子),然后将2m个相同的小球放进去,允许空盒子存在(将这些小球放的盒子标号从大到小连起来就组成了一个满足条件的数组),由隔板法得方案数为\(C({2m+n-1},{n-1})\)
小球组合
组合数学
代码
包含求逆元和求阶乘的技巧
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int fac[2001];
int inv[2001];
int sum1[1001];
int sum2[1001];
const int p=1e9+7;
int qpow(int a,int b)
{
if(b==0)return 1;
if(b%2==0)
{
int x=qpow(a,b/2)%p;
return x*x%p;
}
return a*qpow(a,b-1);
}
void getinv()
{
fac[0]=inv[0]=1;
for(int i=1;i<=2000;i++)
{
fac[i]=fac[i-1]*i%p;
inv[i]=qpow(fac[i],p-2)%p;
}
}
int getC(int n,int m)
{
return fac[n]*inv[n-m]%p*inv[m]%p;
}
main(void)
{
int n=read();
int m=read();
int x=2*m+n-1;
//c(n-1,2*m+n-1)
getinv();
cout<<getC(x,n-1);
}
D. Minimax Problem
题目大意
给出一个n*m的矩阵,我们用maze[n][m]来表示每一个元素,现在我们需要选出其中 i 和 j 两行,i 和 j 可以相同,用这两行的元素构成一个新的数组a,构造规则为a[k]=max(maze[i][k],maze[j][k]),现在我们要使数组a中的最小值最大,请问该如何选择 i 和 j 才能满足条件
题目分析
读完题目后感觉乱糟糟的,但静下心来分析一下,要求最小值的最大值,显然的二分,所以我们很直接的确定下来要对于数组a的最小值进行二分了,但check函数该怎么写是我们接下来需要考虑的问题了,其实当我们确定下来了最小值之后,就相当于对原矩阵构成了一种约束条件,换句话说,原矩阵中值小于当前限制的元素,我们是无法选择的了,不然最后构成的数组a中的最小值就无法保证为当前二分的答案了,有了这样一个转换后,我们就可以将原矩阵中大于当前二分的答案的位置都置为1,其余位置置为0,这样就很自然的进行了状态压缩,因为每一行的元素都是绑定的,也就是对于某一行来说,我们只能选择或者不选,这样又是一种标准的枚举子集,我们再看一眼数据范围,发现m最大只有7,我们如果以枚举子集的形式来确定 i 和 j 的话,时间复杂度最大也只有2^14,所以我们可以直接两层for循环枚举子集状态,找到一种 i 和 j 的情况,满足其能覆盖整个子集就好了,具体实现看代码吧,代码写的比较清楚了
代码
const int inf=1e9;
inline int read()
{
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int mp[300005][10];
int vis[100000];
int ans1,ans2,ans;
int n,m;
bool check(int x)
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
int val=0;
for(int j=1;j<=m;j++)
{
if(mp[i][j]>=x)
val|=1<<(j-1);
}
vis[val]=i;
}
for(int i=0;i<(1<<m);i++)
{
if(vis[i])
{
for(int j=0;j<(1<<m);j++)
{
if(vis[j])
{
if((i|j)==(1<<m)-1)
{
if(ans<=x)
{
ans1=vis[i];
ans2=vis[j];
ans=x;
}
return true;
}
}
}
}
}
return false;
}
main(void)
{
n=read();
m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
mp[i][j]=read();
}
int l=0;
int r=inf;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
l=mid+1;
}
else
{
r=mid-1;
}
}
printf("%d %d\n",ans1,ans2);
}