2019牛客暑期多校训练营(第一场)

题目

https://ac.nowcoder.com/acm/contest/881#question

B Integration (裂项,找规律)

比较详细的题解

https://blog.csdn.net/ftx456789/article/details/96451366?tdsourcetag=s_pctim_aiomsg

题意

求解
1 π ∫ 0 ∞ 1 ∏ i = 1 n ( a i 2 + x 2 ) d x \frac 1\pi \int _0^∞{\frac1{\prod_{i=1}^n{(a_i^2+x^2)}}}dx π10i=1n(ai2+x2)1dx

1 ∏ i = 1 n ( a i 2 + x 2 ) \frac 1{\prod_{i=1}^n{(a_i^2+x^2)}} i=1n(ai2+x2)1裂项为 c 1 a 1 2 + x 2 + c 2 a 2 2 + x 2 . . . . . . \frac {c1}{a_1^2+x^2}+\frac {c2}{a_2^2+x^2}...... a12+x2c1+a22+x2c2......

求解当n=1,n=2情况下c的值,可以找出规律

c i = 1 ∏ i = 1 n ( a j 2 − a i 2 ) ( 1 ≤ j ≤ n 且 j ! = i ) c_i=\frac {1}{\prod _{i=1}^{n}{(a_j^2-a_i^2)}}(1\leq j \leq n 且j!=i) ci=i=1n(aj2ai2)1(1jnj!=i)

再根据积分公式 ∫ 0 ∞ 1 a 2 + x 2 d x = π 2 a \int_0^∞ \frac 1 {a^2+x^2}dx=\frac \pi {2a} 0a2+x21dx=2aπ

即可求解

注意!
计算减法的时候一定要记得+mod


#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn = 1e5+10;
const ll mod=1e9+7;
ll inverse(ll a)
{
  return a == 1 ? 1 : 1LL * (mod - mod / a) * inverse(mod % a) % mod;
}

ll a[maxn];

int main() {

    int n;
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            ll fz=1;
            ll fm=1;
            for(int j=1;j<=n;j++)
            {
                if(j!=i)
                {
                    fm=(fm*(a[j]*a[j]%mod-a[i]*a[i]%mod+mod))%mod;
                }
            }
            fm=fm*a[i]%mod*2%mod;
            ans=(ans+inverse(fm))%mod;
        }
        //ans=ans*inverse(2)%mod;
        printf("%lld\n",ans);


    }
    return 0;
}

E

题意

给n和m,求所有由n个AB和m个BA作为子序列构成的串有多少个,结果mod 1 0 9 + 7 10^9+7 109+7

思路

先假设已经放了n个A,那么下一个放的必须是B,否则无法构成想要的串。
那么由此可知,当A的数量少于n个的时候可以随意放A,因为这些A是必须的,但是一旦当A超过了n,就必须要考虑此时B的数量。
再假设若已经放了n个A和一个B,那么接下来至多只能放一个A,然后就得放B。
因此可以知道 当i-n<=j时可以放A
B同理
根据这样的条件可以写出dp转移方程
d p [ i + 1 ] [ j ] = d p [ i + 1 ] [ j ] + d p [ i ] [ j ] ( i + 1 − n ≤ j ) dp[i+1][j]=dp[i+1][j]+dp[i][j] (i+1-n\leq j) dp[i+1][j]=dp[i+1][j]+dp[i][j](i+1nj)
d p [ i ] [ j + 1 ] = d p [ i ] [ j + 1 ] + d p [ i ] [ j ] ( j + 1 − m ≤ i ) dp[i][j+1]=dp[i][j+1]+dp[i][j](j+1-m\leq i) dp[i][j+1]=dp[i][j+1]+dp[i][j](j+1mi)
另外用memset会超时,只能for循环


#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
const int maxn = 2e3+100;
const ll mod=1e9+7;


ll dp[maxn][maxn];

int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        for(int i=0;i<=n+m;i++)
        {
            for(int j=0;j<=n+m;j++)
               {
                  dp[i][j]=0;
               }
        }
        dp[0][0]=1;
        for(int i=0;i<=n+m;i++)
        {
            for(int j=0;j<=n+m;j++)
            {
                if(i+1-n<=j)
                    dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
                if(j+1-m<=i)
                    dp[i][j+1]=(dp[i][j+1]+dp[i][j])%mod;

            }
        }
        printf("%lld\n",dp[n+m][n+m]);
    }
    return 0;
}

H-XOR

题意

求n个元素中,异或和为0的子集大小之和

思路

https://www.cnblogs.com/Yinku/p/11212303.html

首先求所有元素的线性基B,其秩为r,由于剩下的数构成的子集的异或和总能在线性基里找到唯一对应,那么我们很容易知道所有异或和为0的子集个数为 2 n − r 2^{n-r} 2nr,那么要求这些子集的大小之和就需要去求出每一个数的贡献。

①对于未插入线性基的(n-r)个数来说,每一个数所能构成的子集个数为 2 n − r − 1 2^{n-r-1} 2nr1
那么这个数的贡献就为 2 n − r − 1 2^{n-r-1} 2nr1,那么这(n-r)个数的总贡献就为 ( n − r ) 2 n − r − 1 (n-r)2^{n-r-1} (nr)2nr1

②再考虑插入线性基的r个数,每一个数的贡献为他在①中被使用的次数。要求他被使用的次数,需要将除他以外的(n-1)个数求线性基B2,再将他插入B2,若插入成功,则证明B2不存在组合与他异或和为0,那么他也就没有贡献。若插入失败,则他的贡献为 2 n − r − 1 2^{n-r-1} 2nr1,因为B2的秩必定也为r。

剩下的就是优化问题,在构造B的时候,将无法插入B的数插入到B1中。那么在②构造B2时,先复制B1,然后再将其他数插入,这样就快了很多。

#include<bits/stdc++.h>
#include<stdlib.h>
#include<string>
using namespace std;
typedef long long int ll;
const int maxn = 1e5+100;
const ll mod=1e9+7;
const int N=63;
struct LinearBase{
    ll base[maxn];              //线性基
    int cnt;                    //插入的元素个数
    void init()
    {
        memset(base,0,sizeof(base));
        cnt=0;
    }
    void copy(LinearBase a)
    {
        cnt=a.cnt;
        memcpy(base,a.base,sizeof(base));
    }
    bool insert(ll x)
    {
           for (int j = N; ~j; --j)			//N为最大位数
           {
               if (((x >> j) & 1))
               {
                   if (base[j]) x ^= base[j];
                   else
                   {
                       base[j] = x;
                       cnt++;
                       return true;
                   }
               }
           }
           return false;
    }

}B,B1,B2;

ll a[maxn];
bool vis[maxn];
void ini()
{
    B.init();
    B1.init();
    B2.init();
    memset(vis,0,sizeof(vis));
}

ll qmod(ll a,ll b){
    ll c=1;
    while(b)
    {
        if(b&1)
        {
            c=c*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    return c%mod;
}

int main()
{

    int n;
    while(scanf("%d",&n)!=EOF)
    {
        ini();
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            if(B.insert(a[i]))
            {
                vis[i]=1;
            }
            else
            {
                B1.insert(a[i]);
            }
        }
        ll ans=0;
        ll p2=0;
        if(n!=B.cnt){
            p2=qmod(2,(n-B.cnt-1))%mod;
            ans=p2*(n-B.cnt)%mod;
        }

        for(int i=1;i<=n;i++)
        {
            if(vis[i])
            {
                B2.copy(B1);
                for(int j=1;j<=n;j++)
                {
                    if(i!=j&&vis[j])
                    {
                        B2.insert(a[j]);
                    }
                }
                if(!B2.insert(a[i]))
                {
                    ans=(ans+p2)%mod;
                }
            }
        }
        printf("%lld\n",ans);
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值