充满bug的hu测(1.14)

上午loli只给了我们三个半小时
给了我们三道没有文件名且来路不明的题
评测的时候没有“忽略行末空格和回车”,导致分数非常的鬼。。。

三道题的码量都比较小
但是思维难度还是比较大的

T1

T2

这里写图片描述
这里写图片描述
这里写图片描述

分析:
考虑每条边的贡献
每条边可以把整棵树分成两部分:设左边有 a 个乘客b辆车,右边有 c 个乘客d辆车
那么这条边的贡献系数就是 min(a,d)+min(b,c)

为什么呢?
显然这个贡献系数是个上界,假设存在一种配对方式,使得这条边贡献系数达不到上限,
那么一定存在两个匹配,分别在这条边的两边,交叉一下ta们显然能得到更优的解

枚举每条边,考虑左边子树大小是 x ,右边是nx
因为 min(a,d) min(b,c) 计算方式,我们在这里讨论一下 min(a,d) 的计算方式
左边子树有s辆车的方案数是这里写图片描述
(从m中选择s辆车,这s辆车随机分布在这x个点上,剩下的m-s辆车分布在右子树上)

我们计算出前缀和就可以算出:左边子树小于s辆车的方案
因为这里写图片描述
枚举 i <script type="math/tex" id="MathJax-Element-44">i</script>,再乘上左边多于i辆车,右边多于i个人的方案数即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

const ll p=1e9+7;
const int N=2503;
int n,m;
struct node{
    int x,y,v,nxt;
};
node way[N<<1];
int st[N],tot=0,sz[N];
ll ans=0,a[N],b[N],C[N][N];

void add(int u,int w,int z)
{
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].v=z;way[tot].nxt=st[u];st[u]=tot;
    tot++;
    way[tot].x=w;way[tot].y=u;way[tot].v=z;way[tot].nxt=st[w];st[w]=tot;
}

ll KSM(ll a,ll b)
{
    ll t=1; 
    while (b)
    {
        if (b&1) t=(t%p*a%p)%p;
        b>>=1;
        a=(a%p*a%p)%p;
    }
    return t;
}

ll c(int n,int m)
{
    if (n<m||n<0) return 0;
    else return C[n][m];
}

ll cal(int x)   //左边的子树大小为x
{
    int s=n-x;  //右边的子树大小
    ll num1=1,num2=KSM(s,m),inv=KSM(s,p-2); 
    for (int i=0;i<=m;i++)
    {
        a[i]=num1*num2%p*c(m,i)%p;
        num1=num1*x%p;
        num2=num2*inv%p;
    }
    for (int i=m-1;i;i--) a[i]=(a[i]+a[i+1])%p;
    num1=1;
    num2=KSM(x,m);
    inv=KSM(x,p-2);
    for (int i=0;i<=m;i++)
    {
        b[i]=num1*num2%p*c(m,i)%p;
        num1=num1*s%p;
        num2=num2*inv%p;
    }
    for (int i=m-1;i;i--) b[i]=(b[i]+b[i+1])%p;
    ll ans=0;
    for (int i=1;i<=m;i++) ans=(ans+a[i]*b[i])%p;
    return 1LL*2*ans%p;
} 

void dfs(int now,int fa)
{
    sz[now]=1;
    for (int i=st[now];i;i=way[i].nxt)
        if (way[i].y!=fa)
        {
            dfs(way[i].y,now);
            sz[now]+=sz[way[i].y];
            ans=(ans+(ll)cal(sz[way[i].y])*way[i].v)%p;
        }
}

int main()
{
    scanf("%d%d",&n,&m);

    C[0][0]=1; C[1][0]=1; C[1][1]=1;
    for (int i=2;i<=n;i++)
    {
        C[i][0]=1;
        for (int j=1;j<=n;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;    //预处理一下组合数 
    }

    for (int i=1;i<n;i++)
    {
        int u,w,z;
        scanf("%d%d%d",&u,&w,&z);
        add(u,w,z);
    }
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}

T3

这里写图片描述
这里写图片描述

分析:
这道题相当于有n个点0…n-1,第i个点的两个出边是i*2%n,(i+2+1)%n,求一条哈密顿回路

哈密顿回路
哈密顿图(哈密尔顿图)是一个无向图,由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次

可以给每个点恰好一条出边和一条入边,这样就构成了若干个环
可以发现,连边后是一个大环的充分条件是:对于i=0…(n/2-1),i与i+n/2在一个连通块中

证明:
考虑0与n/2,因为ta们在一个连通块内,而且ta们的出边一样,所以0与1在一个连通块内,再考虑1与n/2+1,可以发现1和2和3都在一个连通块中

于是我们一开始令i的出边是2*i,i+n/2的出边是2*i+1,
枚举i,如果i与n/2+i不在一个连通块内,就交换ta们的出边,这样就并成一个大环了

可以用并查集维护

tip

出题人到底是怎么“相当于”出来的,我也不是很清楚。。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define ll long long

using namespace std;

int n,a[1002];

int main()
{
    scanf("%d",&n);
    int m=n/2;
    int k=(m+1)/2;
    for (int i=0;i<m;i++)
        if (i<k) a[i]=2*i+1,a[i+m]=2*i;
        else a[i]=2*i,a[i+m]=2*i+1;
    for (int i=0;i<m;i++)
    {
        bool ff=0;
        for (int j=a[i];j!=i;j=a[j])
            if (j==i+m) {
                ff=1;
                break;
            }
        if (!ff) swap(a[i],a[i+m]);
    }
    for (int i=a[0];i;i=a[i]) printf("%d",i&1);
    printf("0\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值