欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

牛客网暑期ACM多校训练营(第八场

程序员文章站 2022-04-02 18:51:38
...

B Filling pools
别问我为什么,google就完事了。
贴一个地址。
https://math.stackexchange.com/questions/2732802/computing-nth-schr%C3%B6der-number

#include<bits/stdc++.h>
using namespace std;
const int maxn=300000;
const int mod=998244353;
long long ans[maxn];
int inv[maxn];
void invTable(int n, int p, int inv[]) {
    inv[1] = 1;
    for(int i = 2; i <= n; ++i)
        inv[i] = 1LL*(p-p/i) * inv[p%i] % p;
}
int main()
{
    invTable(maxn-1,mod,inv);
    int n;
    scanf("%d",&n);
    ans[0]=1;
    ans[1]=2;
    for(int i=2;i<=n;i++)
        ans[i]=(((6*i-3)%mod*ans[i-1]%mod-(i-2)*ans[i-2]%mod)%mod+mod)%mod*inv[i+1]%mod;
    printf("%lld\n",ans[n-1]);
}

E Touring cities
我们可以分类讨论一下,可以发现当n或者m是偶数的时候,转一圈就能回来了,时间即为n*m,而当n和m都为奇数时,最多要n*m+1,我们再想,最后一个点会是哪一些,如果最后一个点的有一条直接到(1,1)的路径,那么时间也可以为n*m了。我们画图又可以发现,如果把整个图的点按照黑白相间的方式染色((1,1)为黑色),那么最后一个点都是黑点。
一开始我就按照上面的方式写了,然而错了。。看了题解才发现其实如果黑色和黑色相连的话,那么时间就能为n*m了。。

#include<bits/stdc++.h>
using namespace std;
int mp[105][105];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(mp,0,sizeof(mp));
        int n,m,k;
        scanf("%d %d %d",&n,&m,&k);
        int x1,y1,x2,y2;
        bool flag=0;
        for(int i=1;i<=k;i++)
        {
            scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
            if(x1!=x2||y1!=y2)
                if((x1+y1)%2==0&&(x2+y2)%2==0)
                flag=1;
        }
        if(n%2==0||m%2==0||flag)
            printf("%d\n",n*m);
        else printf("%d\n",n*m+1);
    }


}

H Playing games
根据nim博弈的结论来看,我们实际上就是要找到最多的数,他们异或和为0.假设这n个数异或和为v,那么问题又可以进行转换,就是找出最少的数ans他们的异或和为v,那么答案就为n-ans
怎么找呢,这里有两点要提。
1,是ans至多也就19个,因为都不大于1<<19(感觉很有道理但不知道怎么证明)
2,FWT的数学含义需要知道。
我们这里就可以进行二分ans啦,然后再用FWT判断是否可以找到一种方案异或和为v。

#include<bits/stdc++.h>
using namespace std;
const int maxa=(1<<19);
const int mod=1e9+7;

int quick_mul(int a,int b,int mod)
{
    int res=1;
    while(b)
    {
        if(b%2)res=1LL*res*a%mod;
        b/=2;
        a=1LL*a*a%mod;
    }
    return res;
}
int a[maxa];
int b[maxa];
int inv2=5e8+4;
void FWT(int a[],int n,int flag){
    for (int d=1;d<n;d<<=1)
        for (int i=0;i<n;i+=(d<<1))
            for (int j=0;j<d;j++){
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=(x+y)%mod;
                a[i+j+d]=(x-y)%mod;
                if (flag==-1){
                    a[i+j]=1LL*a[i+j]*inv2%mod;
                    a[i+j+d]=1LL*a[i+j+d]*inv2%mod;
                }
            }
}
int v;
bool check(int k)
{
    for(int i=0;i<maxa;i++)
        b[i]=quick_mul(a[i],k,mod);
    FWT(b,maxa,-1);
    b[v]%=mod;
    b[v]=(b[v]+mod)%mod;
    if(b[v])return 1;
    return 0;
}

int main()
{
    int n;
    scanf("%d",&n);
    v=0;
    int x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        a[x]++;
        v^=x;
    }
    if(v==0)
    {
        printf("%d\n",n);
        return 0;
    }
    a[0]=1;
    FWT(a,maxa,1);
    int l=1,r=19;
    int ans;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    printf("%d\n",n-ans);
    return 0;


}

这里还有一点要提,为了使得这个具有单调性,我们需要使a[0]>0,a代表的是下标为i出现的次数,根据fwt的数学含义看,我们要拿那个二分的次数时,需要把之前的结果也存起来,也就相当于有一次拿了0,所以我们必须使a[0]>0,才能使二分具有单调性。

相关标签: 牛客 多校