KEYENCE Programming Contest 2019

D - Double Landscape
考虑某一行某一列被限制后,该行列只能填小于等于被限制的数。

没被限制的行列可以任意填,由于先填大的不影响后续填小的,所以考虑从大到小填数。

如果这个数要被限制填在某一行或某一列,就填在某一行或列。

否则可以任意填在限制最大值比它大的位置,如果填不了就-1。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,mod=1e9+7;
int n,m,x[N*N],y[N*N],a[N][N],sum[N*N];
bool vis[N*N];
int main()
{
    
    
    scanf("%d%d",&n,&m);
    bool flag=true;
    for(int i=1;i<=n;i++)
    {
    
    
        int s;scanf("%d",&s);
        if(x[s]) flag=false;
        x[s]=i;
        vis[s]=true;
    }
    for(int i=1;i<=m;i++)
    {
    
    
        int s;scanf("%d",&s);
        if(y[s]) flag=false;
        y[s]=i;
        vis[s]=true;
    }
    for(int i=n*m;i>=1;i--)
    {
    
    
        if(x[i])
            for(int j=1;j<=m;j++) a[x[i]][j]=i;
        if(y[i])
            for(int j=1;j<=n;j++) a[j][y[i]]=i;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(a[i][j]) sum[a[i][j]]++;
    else sum[n*m+1]++;
    ll ans=1;
    for(int i=n*m;i>=1;i--)
    {
    
    
        if(vis[i])
        {
    
    
            if(!sum[i]) flag=false;
            if(!(x[i]&&y[i])) ans=ans*sum[i]%mod;
            sum[i]--;
            sum[i]+=sum[i+1];
        }
        else
        {
    
    
            sum[i]+=sum[i+1];
            if(!sum[i]) flag=false;
            ans=ans*sum[i]%mod,sum[i]--;
        }
    }
    if(!flag) ans=0;
    printf("%lld\n",ans);
}

E Connecting Cities
分治,考虑一段区间l,r,在l,mid区间中找一个最小的i0

mid+1,r区间找一个最小的j0

用i0给右区间全部连边,j0给左区间全部连边

对于边i,j,有边i0,j,边i0,j0,边i,j0都比它小

而一个环中的最大边不会是最小生成树中的边。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
int n,tot,d,a[N],f[N];
struct node
{
    
    
    int u,v;
    node(int u=0,int v=0,ll w=0):u(u),v(v),w(w){
    
    }
    ll w;
    bool operator<(const node&o)const
    {
    
    
        return w<o.w;
    }
}e[N*20];
void solve(int l,int r)
{
    
    
    if(l==r) return;
    int m=l+r>>1;
    ll p1=l,p2=m+1,s=1e18;
    for(ll i=l;i<=m;i++)
        if(-i*d+a[i]<s)
        s=-i*d+a[i],p1=i;
    s=1e18;
    for(ll i=m+1;i<=r;i++)
        if(i*d+a[i]<s)
        s=i*d+a[i],p2=i;
    for(ll i=l;i<=m;i++)
    {
    
    
        tot++;
        e[tot]=node(i,p2,-i*d+a[i]+p2*d+a[p2]);
    }
    for(ll i=m+1;i<=r;i++)
    {
    
    
        tot++;
        e[tot]=node(p1,i,-p1*d+a[p1]+i*d+a[i]);
    }
    solve(l,m);solve(m+1,r);
}
int getf(int x){
    
    return f[x]==x?x:f[x]=getf(f[x]);}
int main()
{
    
    
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    solve(1,n);
    sort(e+1,e+1+tot);
    for(int i=1;i<=n;i++) f[i]=i;
    ll ans=0;
    for(int i=1;i<=tot;i++)
    {
    
    
        int fu=getf(e[i].u),fv=getf(e[i].v);
        if(fu==fv) continue;
        ans+=e[i].w;
        f[fu]=fv;
    }
    printf("%lld\n",ans);
}

F Paper Cutting
从n+m中选择k条线进行全排列的方案数是C(n+m,k)*f[k]。

考虑计算随机进行选k条线进行切割的期望,答案就是期望乘以所有方案。

考虑一个以i,j为左下角的矩形,它第一次出现时得到的期望是1。

以后只要进行一次切割,期望就加1。

那么对于一个以i,j为左下角的矩形在k次操作里面出现的概率为C(k,2)/C(n+m,2)

其中C(k,2)为把这两条线放入k次序列的方案数(不考虑顺序,因为只有两个同时出现才有贡献)

而能排列的前提是这两个被同时选中了,同时选中的概率是1/C(n+m,2),乘起来就是概率

现在考虑随后每切一次的概率,考虑i,j,k,如果k在i,j后面出现,那么i,j这个矩形的贡献就加1。

那么在其后面的概率为1/3(可以是任意一个在其后面)

三个同时被选中的概率为C(k,3)/C(n+m,3),同时k的候选个数有除了i,j还有n+m-2个

所以总期望是ans=C(k,2)/C(n+m,2)+C(k,3)/C(n+m,3)*1/3*(n-m+2)

而i,j配对的方案数是n*m

所以对于i>0,j>0的总方案数是n*m*ans

其它还有i>0,j=0 i=0,j>0 i=0,j=0的情况,可以用同样的方法推出来。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e7+5,mod=1e9+7;
int n,m,k;
ll f[N],invf[N];
ll inv(ll x){
    
    return x<=1?x:(mod-mod/x)*inv(mod%x)%mod;}
ll C(ll n,ll m)
{
    
    
    return m<=n?f[n]*invf[n-m]%mod*invf[m]%mod:0;
}
int main()
{
    
    
    f[0]=invf[0]=f[1]=invf[1]=1;
    for(int i=2;i<N;i++) f[i]=f[i-1]*i%mod,invf[i]=(mod-mod/i)*invf[mod%i]%mod;
    for(int i=2;i<N;i++) invf[i]=invf[i]*invf[i-1]%mod;
    scanf("%d%d%d",&n,&m,&k);
    ll ans=C(k,2)*inv(C(n+m,2))%mod+C(k,3)*inv(C(n+m,3))%mod*(n+m-2)%mod*inv(3)%mod;
    ans=ans*n%mod*m%mod;
    ll res=C(k,1)*inv(C(n+m,1))%mod+C(k,2)*inv(C(n+m,2))%mod*(n+m-1)%mod*inv(2)%mod;
    ans=(ans+res*(n+m))%mod;
    ans=(ans+k)%mod;
    ans=ans*C(n+m,k)%mod*f[k]%mod;
    printf("%lld\n",ans);
}

猜你喜欢

转载自blog.csdn.net/Huah_2018/article/details/104172607