11.5离线赛

一、放数字游戏

数据:对于70%,n∈[1,2000];
对于100%,n∈[1,200000],Ai∈[0,108];

没想到第一题这么难,推了好久的公式。先设dp[i]表示前i个数的所有方案的和。当前加入当前有x个数,放一个数a进去,可以放在左边或右边。这样就有两个dp[i-1]。再计算a和相邻数的乘积。手算一下发现有一个规律:A[0] * 2+A[1] * 2+A[2] * 4+A[3] * 8……这样就很简单了,只要再加一个A[i] * cnt[i] *2,因为有左右两边。

for(int i=0;i<=n;i++){
    scanf("%lld",&A[i]);
    if(i)cnt[i]=cnt[i-1];
    cnt[i]=(cnt[i]+A[i]*C[i]%Mod)%Mod;//按照公式推的
}
for(int i=1;i<=n;i++){
    dp[i]=(dp[i-1]*2%Mod+A[i]*cnt[i-1]%Mod)%Mod;//公式
}

二、电缆建设

数据:对于70%,n,m∈[1,100000]
对于100%,n,m∈[1,1000000]

看完题感觉就是一个最小生成树,只要把边造出来就行了。看了一眼数据发现n*m的只有40分。再想想发现边太多了,很多都没有用,可以谈心的去一些必要的较短边,每个点只要和附近的点相连就行了,这样也只有70分。为什么?因为n、m的数据可是nlogn都卡的掉。那么只能O(n)了。

O(n)用最小生成树?没听说过。想都想不到这道题竟然是一道dp。定义dp[i][j][2],表示第一行第i个位置,第二行第j个位置,此时i、j不连通/连通(0/1)。还有一点是要知道的是,连边的时候不能有交叉(可以看成是一个三角形,两边之和大于第三边),这样转移就很简单了。

dp[i][j][1]=min(f[i-1][j][1]+a[i]-a[i-1],
 dp[i-1][j][1]+dist(i,j),
 dp[i-1][j][0]+a[i]-a[i-1]+dist(i,j))
dp[i][j][0]=min(f[i-1][j][1],
 dp[i-1][j][0]+a[i]-a[i-1])

可以自己理会一下。其他的就不是重点了。

三、抢座位

数据:
这里写图片描述

计蒜客上的题。
前30分直接全排列加判断。然后的20分可以用状压。dp[i][j]表示第i个数j状态。只需要在i是特殊点时特判一下就行(找到第一个可行的位置就停下)。

memset(dp,-1,sizeof dp);
dp[0][0]=1;
for(int i=1;i<=n;i++){
    int st=B[i];if(!st)st=1;
    for(int j=0;j<(1<<n);j++){
        if(dp[i-1][j]==-1)continue;
        for(int k=st;k<=n;k++){
            if(j&(1<<(k-1)))continue;
            int now=j|(1<<(k-1));
            if(dp[i][now]==-1)dp[i][now]=0;
            dp[i][now]=(dp[i][now]+dp[i-1][j])%Mod;//累加
            if(B[i])break;//找到就退出
        }
    }
}

然后是m==0的,只要阶乘。然后是m==1的。
当有一个点的时候,先把点放在给出的位置,这时其他点都可以随便放,那也是阶乘。然后使这个点往后移一位,这是必须要放一个点在刚刚的位置,而且要保证放的点的编号在特殊点的前面。这是要好多点。那么就是组合数和阶乘。同理再往后也是一样的。

那么当m不只是1的时候,做法其实是差不多的。
下面代码里B是阶乘,C是组合数,提前预处理。

void f(int x,ll sum,int k,int now){//第几个点 现在的方案数 固定了几个位置 哪些位置被用 
    if(x==m+1){//边界 
        ans=(ans+sum*B[n-k]%Mod)%Mod;
        return;
    }
    int t=A[x].x-1-k,y=0,t1=0;//t现在可以动的点数
    if(t<0)return;
    for(int i=A[x].y;i<=min(n,A[x].x+A[x].y-1);i++,y++){
        if(y<0)return;
        if(now&(1<<(i-1))){y--;continue;}
        now|=(1<<(i-1));t1++;//可以放在i点 可放个数加一
        f(x+1,sum*C[t][y]%Mod*B[y]%Mod,k+t1,now);
        //下个数  现在的方案     已用位置    状态 
    }
}

第一题出乎意料的难。第二题卡常卡的很辛苦(最后还是差72ms)。第三题debug好久(long long+Mod)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值