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

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

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

第七场和第八场比较难,所以补的题少一点。。
C Bit Compression
暴力+剪枝就完事了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int num[20][maxn];
char s[maxn];

int C(int opt,int a,int b)
{
    if(opt==0)return a&b;
    else if(opt==1)return a|b;
    return a^b;
}

int dfs(int n)
{
    int x=0;
    for(int i=0;i<(1<<n);i++)
        x+=num[n][i];
    if(x==0)return 0;
    if(n==0)return 1;
    int res=0;
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<(1<<(n-1));j++)
            num[n-1][j]=C(i,num[n][2*j],num[n][2*j+1]);
        res+=dfs(n-1);
    }
    return res;
}

int main()
{
    int n;
    scanf("%d %s",&n,s);
    for(int i=0;i<(1<<n);i++)
        num[n][i]=s[i]-'0';
    printf("%d\n",dfs(n));
    return 0;
}

E Counting 4-Cliques
这题我其实觉得题解并没有说到位,为什么要留下5个点来进行最后的填补,这其实有一定的原因的。
首先,我们可以预处理出n个点的完全图的4元环的个数,也就是C(n,4),那么我们可以发现,70和71个点之间的差距最大,差不多60000,所以我们面临的最大的问题就是如何用5个点来构造60000个四元环,只要这个问题解决了,那么其他的构造就不成问题了。

#include<bits/stdc++.h>
using namespace std;
map<int,int>M;

int C(int n,int m)
{
    if(n<m)return 0;
    long long up=1;
    for(int i=0;i<m;i++)
        up=up*(n-i);
    long long down=1;
    for(int i=1;i<=m;i++)
        down=down*i;
    return up/down;
}
int vis[70000];
void init()
{
    for(int i=4;i<=70;i++)
    {
        int tmp=C(i,4);
        M[tmp]=i;
        //cout<<i<<' '<<tmp<<endl;
    }
    vis[1]=3;
    for(int i=4;i<=70;i++)
        vis[C(i,3)]=i;
}

int main()
{
    init();
    int k;
    scanf("%d",&k);
    auto it=M.upper_bound(k);
    it--;
    int left=k-(it->first);
    if(left==0)
    {
        int n=(it->second);
        printf("%d %d\n",n,n*(n-1)/2);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            printf("%d %d\n",i,j);
    }
    else
    {
        vector<int>V;
        bool flag=0;
        int n=(it->second);
        for(int i=2;i<=n&&!flag;i++)
            for(int j=2;j<=n&&!flag;j++)
            for(int t=2;t<=n&&!flag;t++)
            for(int m=2;m<=n&&!flag;m++)
        {
            int ct=C(i,3)+C(j,3)+C(t,3)+C(m,3);
            if(ct>left)break;
            int tmp=left-ct;
            if(tmp>=70000)continue;
            if(tmp==0)
            {
                flag=1;
                if(i>2)V.push_back(i);
                if(j>2)V.push_back(j);
                if(t>2)V.push_back(t);
                if(m>2)V.push_back(m);
                break;
            }
            else if(vis[tmp])
            {
                flag=1;
                if(i>2)V.push_back(i);
                if(j>2)V.push_back(j);
                if(t>2)V.push_back(t);
                if(m>2)V.push_back(m);
                V.push_back(vis[tmp]);
                break;
            }
        }
        int m=n*(n-1)/2;
        n=n+V.size();
        for(int i=0;i<V.size();i++)
            m+=V[i];
        printf("%d %d\n",n,m);
        for(int i=1;i<=(it->second);i++)
            for(int j=i+1;j<=(it->second);j++)
            printf("%d %d\n",i,j);
        for(int i=0;i<V.size();i++)
        {
            int now=i+1+(it->second);
            for(int j=0;j<V[i];j++)
                printf("%d %d\n",now,j+1);
        }
    }
    return 0;
}

J Sudoku Subrectangles
这题真好。
首先很容易想到52*52*n*m,显然会超时。在分析这题的时候,会发现以一个可行区域来往后推要考虑很多限制。而有些限制是前面会影响后面的。所以我们在枚举的时候沿着限制的影响方向走就会减少时间复杂度,因为这样就不用重新考虑前面的影响了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
char mp[maxn][maxn];
int L[maxn][maxn];
int U[maxn][maxn];
int n,m;
void init()
{
    int Nxt[maxn];
    for(int i=1; i<=n; i++)
    {
        memset(Nxt,inf,sizeof(Nxt));
        for(int j=m; j>=1; j--)
            L[i][j]=min(L[i][j+1]+1,Nxt[mp[i][j]]-j),Nxt[mp[i][j]]=j;
    }
    for(int i=1; i<=m; i++)
    {
        memset(Nxt,0,sizeof(Nxt));
        for(int j=1; j<=n; j++)
        {
            U[j][i]=min(U[j-1][i]+1,j-Nxt[mp[j][i]]),Nxt[mp[j][i]]=j;
        }
    }
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1; i<=n; i++)
        scanf("%s",mp[i]+1);
    init();
    int Len[maxn];
    long long ans=0;
    for(int i=1;i<=m;i++)
    {
        memset(Len,inf,sizeof(Len));
        for(int j=1;j<=n;j++)
        {
            for(int k=0;k<L[j][i];k++)
            {
                Len[k]=min(Len[k]+1,U[j][i+k]);
                if(k)Len[k]=min(Len[k],Len[k-1]);
                ans+=Len[k];
            }
            for(int k=L[j][i];k<52;k++)Len[k]=0;
        }
    }
    cout<<ans<<endl;
}

D Inverse Inverse Problem
最后是D题,真的是神仙题啊。。
说实话虽然理解了做法,但碰见肯定还是脑子一片糊啊。。太绕了。。
直接讲做法吧。
首先第一点就是做n次f(x)运算可以有一个通式
fn(x)=T=Anx+(1+A+A2+...+An1)B
第二就是当A可以为1的情况有
1,X=T
2,X!=T&&n%p!=0
因为上面的式子其实还要取模,所以当A=1时,B的系数就为n%P,如果n%p=0,的话那么就只剩下前面的x了,x又不等于T,所以不成立。所以n%p!=0.
那么当A不等于1时,式子就可以化为
TAnx=An1A1B
假设已经确定A并且A!=1了,B无解的情况就是左边不等于0,右边等于0.那么我们可以使T=1,X=0.右边也就是An1=0
这个式子其实是在模p的基础上成立的,严格上看实际上是An1(modp)
那么我们要找到最大的A,小于A的数(2,3,…A-1)x都要满足上面的式子。
而上面的式子实际上就是求x的阶,而x的阶其实是(p-1)的约数,大家应该都知道当n等于p-1时这个式子就会满足情况了。
我们记r(x)为x的阶,那么n=lcm(r(2),r(3),..r(A-1))。这样也就求出n了。
所以做法总结上面的思路,就是枚举小于M的模数p,然后在这个模数下面,枚举(p-1)的约数,看在当前约数下能算出来的A最大是多少就行了。
然而我看别人的代码并没有枚举p-1的约数,他们枚举的都是p-1的质因子b,然后n等于(p-1)/b,我不太明白从中的道理,如果有读者知道的,希望您能联系我。。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
bool is[maxn];
int prm[maxn],id;
int md[maxn];//  minimum divisor
void init()
{
    memset(is,1,sizeof(is));
    is[0]=is[1]=0;
    md[1]=1;
    int k=0;
    prm[k++]=2;
    md[2]=2;
    for(int i=4;i<maxn;i+=2)is[i]=0,md[i]=2;
    int e=(int)sqrt(maxn+0.0);
    int i;
    for(i=3;i<=e;i+=2)if(is[i])
    {
        prm[k++]=i;
        md[i]=i;
        for(int s=2*i,j=i*i;j<maxn;j+=s)
        {
            is[j]=0;
            if(!md[j])md[j]=i;
        }
    }
    for(;i<maxn;i+=2)if(is[i])prm[k++]=i,md[i]=i;
    id=k;
}


int factor[100][2];
int fatCnt;
int getFactors(int x)
{
    fatCnt=0;
    int tmp=x;
    for(int i=0;prm[i]<=tmp/prm[i];i++)
    {
        factor[fatCnt][1]=0;
        if(tmp%prm[i]==0)
        {
            factor[fatCnt][0]=prm[i];
            while(tmp%prm[i]==0)
            {
                factor[fatCnt][1]++;
                tmp/=prm[i];
            }
            fatCnt++;
        }
    }
    if(tmp!=1)
    {
        factor[fatCnt][0]=tmp;
        factor[fatCnt++][1]=1;
    }
    return fatCnt;
}

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;
}

typedef tuple<int,int,int>t3;

vector<int>V;
void getfac(int x)
{
    V.clear();
    for(int i=x;i>1;i/=md[i])
        V.push_back(md[i]);
    sort(V.begin(),V.end());
    V.erase(unique(V.begin(),V.end()),V.end());
}

t3 solve(int idx)
{
    int p=prm[idx];
    int x=p-1;
    getfac(x);
    int A=0,n;
    for(int i=0;i<V.size();i++)
    {
        int tmpn=x/V[i];
        int nowa=2;
        while(nowa<p&&quick_mul(nowa,tmpn,p)==1)nowa++;
        if(nowa>A)
        {
            A=nowa;
            n=tmpn;
        }
    }
    return t3(A,n,p);
}


int main()
{
    init();
    int M;
    scanf("%d",&M);
    int n=1,A=1;
    int p=1;
    for(int i=1;i<id;i++)
    {
        //cout<<i<<endl;
        if(prm[i]>M)break;
        tuple<int,int,int>tmp=solve(i);
        if(A<get<0>(tmp))
            tie(A,n,p)=tmp;
    }
    while(n%p)n+=(p-1);
    printf("1 %d 2 %d\n",n,p);

    return 0;

}

最后x不能等于0,你换一下就行了,只要使得左边不等于0.

相关标签: 牛客 多校