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

版权声明:转载之前请说你是博主的粉丝哦。 https://blog.csdn.net/qq_34921856/article/details/81624404

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,才能使二分具有单调性。

猜你喜欢

转载自blog.csdn.net/qq_34921856/article/details/81624404