AtCoder Regular Contest 093

AtCoder Regular Contest 093


C - Traveling Plan

题意:

给定n个点,求出删去i号点时,按顺序从起点到一号点走到n号点最后回到起点所走的路程是多少。

\(n\le 2e5\)

分析:

可以通过观察发现,无论删去那个点,比全部都走所差距的距离都是\(|a_i-a_{i-1}|-|a_{i+1}-a_i|+|a_{i-1}-a_{i+1}|\)
所以直接枚举即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+7;
int a[MAXN],n,ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n+1;i++) ans+=abs(a[i]-a[i-1]);
    for(int i=1;i<=n;i++){
        cout<<ans-abs(a[i]-a[i-1])-abs(a[i]-a[i+1])+abs(a[i-1]-a[i+1])<<endl;
    }
}

D - Grid Components

题意:

求一个边长均小于等于100的矩阵,涂成黑白两色,使得白色连通块和黑色连通块的数量分别是A、B。输出这个矩阵的长宽,并输出这个矩阵的具体涂色方案('.'是白色,'#'是黑色)。

\(1\le A,B \le 500\)

分析:

经过观察发现100×100的矩阵一定可以满足所有要求。

那么这么构造,首先把上下两部分平分开,上面是白色下面是黑色,然后一个一个的在白色里填充黑色,黑色同理。

填充方法是从头开始每隔一行一列就填一个,可以证明这样填上下两个矩阵每个矩阵都可以填600个以上所以可以轻松满足要求。

#include <bits/stdc++.h>
using namespace std;
int a,b;
char ch[201][201];
int main()
{
    for(int i=1;i<=50;i++)
        for(int j=1;j<=100;j++)
            ch[i][j]='.';
    for(int i=51;i<=100;i++)
        for(int j=1;j<=100;j++)
            ch[i][j]='#';
    cin>>a>>b;
    a--;b--;
    int cnt1=0,cnt2=0;
    for(int i=1;i<=50;i+=2){
        if(cnt1==b) break;
        for(int j=1;j<=100;j+=2){
            if(cnt1==b) break;
            cnt1++;
            ch[i][j]='#';
        }
    }
    for(int i=52;i<=100;i+=2){
        if(cnt2==a) break;
        for(int j=1;j<=100;j+=2){
            if(cnt2==a) break;
            cnt2++;
            ch[i][j]='.';
        }
    }
    cout<<"100 100"<<endl;
    for(int i=1;i<=100;i++){
        for(int j=1;j<=100;j++){
            cout<<ch[i][j];
        }
        cout<<endl;
    }
}

E - Bichrome Spanning Tree

题意:

给出一个N个点M条边的无向图,现在要给一些边染黑色或者白色,现在从这些边中选出一些边生成若干棵生成树,要求这些生成树至少包含一条白色的边和一条黑色的边,而且这些生成树的最小值是X,问有多少种染色方法。

分析:

先求一下最小生成树并且设其权值和为s。

那么分情况讨论一下,对于\(s>x\) 的情况,明显答案是0;

对于\(s=x\)的情况,我们可以确定求得那棵树一定是这个最小生成树,所以记录一下所有在最小生成树中的边。那么这些边之外的边就可以随意染色,然后保证最小生成树中的边不是全黑或者全白就可以了。容易得出答案是\(2^{M-m}\times(2^m-1)\)种。

首先必须把S里面的的所有边涂成黑色(或者白色),先假设全部涂成黑色,那么剩余的边,对每一条边单独处理,对边e,它染成白色添加进去之后,如果和集合S中的边形成的满足题意的最小生成树权重w′小于X,那么这条边只能继续涂成黑色;如果w′大于X,它涂成黑色白色都无所谓;然后把w′等于X的边的数量统计下来,假设有tot条边,可以知道这tot条边只要有一条边是白色就行了,因为只要选中这里面的一条边,剩余的边肯定都是从集合S中选出来了, 不可能再从其它边中选取,如果必须涂成黑色的有d条边, 那么答案就是2M−tot−d∗(2tot−1),最终结果再乘以2就行了,就是必须涂成一种颜色的有两种涂法

(借鉴自:AtCoder Regular Contest 093(E-Bichrome Spanning Tree)

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2010;
#define ll long long
#define int ll
const int mo=1e9+7;
struct po
{
    int x,y,l;
}a[MAXN];
bool cmp(po a,po b){return a.l<b.l;}
int n,m,tmp,x;
int f[MAXN];
int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
int kruskal(int now)
{
    int res=0;
    for(int i=1;i<=n;i++) f[i]=i;
    if(now) {res+=a[now].l;f[a[now].x]=a[now].y;}
    for(int i=1;i<=m;i++){
        int r1=find(a[i].x),r2=find(a[i].y);
        if(r1==r2) continue;
        res+=a[i].l;
        f[r1]=r2;
    }
    return res;
}
inline int power(int x,int k)
{
    int cnt=1;
    while(k){
        if(k&1) cnt=cnt*x%mo;
        k>>=1;
        x=x*x%mo;
    }
    return cnt;
}
int val,cnt1,cnt2;
int ans;
main()
{
    cin>>n>>m>>x;
    for(int i=1;i<=m;i++) cin>>a[i].x>>a[i].y>>a[i].l;
    sort(a+1,a+m+1,cmp);
    tmp=kruskal(0);
    if(tmp>x){
        cout<<0;
        return 0;
    }
    if(tmp==x){
        for(int i=1;i<=m;i++){
            val=kruskal(i);
            if(val==x) cnt1++;
            else cnt2++;
        }
        ans=((power(2,cnt1)-2)*power(2,cnt2)%mo+mo)%mo;
    } else {
        for(int i=1;i<=m;i++){
            val=kruskal(i);
            if(val==x) cnt1++;
            else if(val>x) cnt2++;
        }
        ans=(2*(power(2,cnt1)-1)%mo*power(2,cnt2)%mo+mo)%mo;
    }
    cout<<ans;
}

F - Dark Horse

题意:

有2N个选手参与一场比赛,比赛规则是:相邻的两个人比赛一次,败者淘汰掉,胜者继续进行,直到只剩一个人为止。现在给出1号选手会败给哪些选手并且已知其他选手之间均满足:两个选手比赛,编号小的一定会胜利。现在可以安排每个选手初始的位置,要钦定1号选手最后获胜,求能满足条件的初始位置的方案数。

分析:

由于可以明显发现1号无论站在哪里最后的胜负方案数都是一样的,所以可以直接就让1号在一号位置,然后将答案最后乘上\(2^n\)即可。

考虑1号一定会打n场,而且每次都会和\(2^n+1...2^{n+1}\)中编号最小的人打,所以保证其中没有人在ad这个集合内就可以了。

以下部分摘自dalao:

接下来就是本题最有趣的地方了:容斥原理。
设S为N个块的一个子集,\(f(S)\)表示这个子集中所有块的最小值均在A的范围内(其余块是否在A的范围内不考虑)的方案数。

这样一来,最终答案即为\(∑(−1)^{|S|}f(S)\)
将A从大到小排序,依次考虑加入\(A_i\)后的\(f(S)\)
1、成为一个块的最小值:那么在这个块中,必须填充相应数量的,编号比AiAi大的选手,组合数统计。
2、不成为块的最小值,不操作即可\((dp[i][S]+=dp[i−1][S])\)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int MAXN=20;
const int MAXM=2<<16+1;
const int mo=1e9+7;
int n,m,a[MAXN],bit[MAXN],c[MAXM];
int fac[MAXM],inv[MAXM],dp[MAXN][MAXM];
int power(int x,int k)
{
    int cnt=1;
    while(k){
        if(k&1) cnt=cnt*x%mo;
        x=x*x%mo;
        k>>=1;
    }
    return cnt;
}
int C(int x,int y){return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;}
main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++) cin>>a[i];
    reverse(a+1,a+m+1);
    for(int i=0;i<=n-1;i++) bit[i]=1<<i;
    int cnt=1<<n;fac[0]=1;
    for(int i=1;i<=cnt;i++)
        fac[i]=1ll*fac[i-1]*i%mo;
    inv[cnt]=power(fac[cnt],mo-2);
    for(int i=cnt-1;i>=0;i--)
        inv[i]=inv[i+1]*(i+1ll)%mo;
    int u=(1<<n)-1;dp[0][0]=1;
    for(int i=1;i<=m;i++)
        for(int s=0;s<=u;s++){
            int l=cnt-a[i]+1-s;
            dp[i][s]=(dp[i][s]+dp[i-1][s])%mo;
            for(int j=0;j<=n-1;j++){
                if(l<bit[j]) break;
                if(bit[j]&s) continue;
                dp[i][s^bit[j]]=(dp[i][s^bit[j]]+(1ll*dp[i-1][s]*C(l-1,bit[j]-1))%mo*fac[bit[j]]%mo)%mo;
            }
        }
    int ans=fac[cnt-1]; c[0]=1;
    for(int s=1;s<=u;s++){
        c[s]=-c[s-(s&-s)];
        ans=(ans+1ll*c[s]*dp[m][s]*fac[cnt-1-s]%mo)%mo;
    }
    ans=(1ll*ans*cnt%mo+mo)%mo;
    cout<<ans;
}

转载于:https://www.cnblogs.com/victorique/p/9692133.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值