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

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

第七场和第八场比较难,所以补的题少一点。。
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)运算可以有一个通式
f n ( x ) = T = A n x + ( 1 + A + A 2 + . . . + A n 1 ) 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时,式子就可以化为
T A n x = A n 1 A 1 B
假设已经确定A并且A!=1了,B无解的情况就是左边不等于0,右边等于0.那么我们可以使T=1,X=0.右边也就是 A n 1 = 0
这个式子其实是在模p的基础上成立的,严格上看实际上是 A n 1 ( m o d p )
那么我们要找到最大的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.

猜你喜欢

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