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

目录

A 、The power of Fibonacci  (循环节+中国剩余定理)

B 、Quadratic equation  (二次剩余)

D 、Knapsack Cryptosystem  (折半搜索)

E 、All men are brothers  (并查集)


A 、The power of Fibonacci  (循环节+中国剩余定理)

题意:

对斐波那契数列计算   \displaystyle \sum_{i=1}^n F_i^m1 \leq n\leq 10^9,1 \leq m\leq 10^3  ,答案对1e9取模

分析:

很容易想到循环节。

但是难点是循环节太大了,所以导致不好计算。

但是

根据同余得性质

  ,  ,

那么可以将mod(1e9)拆分,

同时:

不单是斐波那契,循环节都可以拆成两个互质的循环节,再通过中国剩余定理合并(取模前提下),因为%pq的循环节可以通过%p和%q的循环节推导。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pid pair<double,double>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rush() int T;scanf("%d",&T);while(T--)
#define sc(a) scanf("%d",&a)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

//#define gc getchar
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read(){
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}

const ll mod2=512;
const ll mod5=1953125;
const ll xun2=768;
const ll xun5=7812500;

ll f2[xun2+10];
ll f5[xun5+10];

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

void exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1;
        y=0;
        return;
    }
    ll tx,ty;
    exgcd(b,a%b,tx,ty);
    ll tmp=tx;
    x=ty;
    y=tmp-(a/b)*ty;
}

//中国剩余定理
ll CRT(ll a[],ll m[],ll n){
    ll M=1;
    ll ans=0;
    for(ll i=1;i<=n;i++)
        M*=m[i];
    for(ll i=1;i<=n;i++){
        ll x,y;
        ll Mi=M/m[i];
        exgcd(Mi,m[i],x,y);
        ans=(ans+Mi*x*a[i])%M;     //Mi*x%m[i]==1.
    }
    if(ans<0) ans+=M;
    return (ans+M)%M;
}

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    ll n,m;
    scanf("%lld%lld",&n,&m);
    f2[0]=0,f2[1]=1;
    f5[0]=0,f5[1]=1;
    for(int i=2;i<xun2;i++){
        f2[i]=(f2[i-1]+f2[i-2])%mod2;
    }
    for(int i=1;i<xun2;i++){
        f2[i]=(f2[i-1]+quick_pow(f2[i],m,mod2))%mod2;
    }
    for(int i=2;i<xun5;i++){
        f5[i]=(f5[i-1]+f5[i-2])%mod5;
    }
    for(int i=1;i<xun5;i++){
        f5[i]=(f5[i-1]+quick_pow(f5[i],m,mod5))%mod5;
    }
    ll a=(n/xun2*f2[xun2-1]%mod2+f2[n%xun2])%mod2;
    ll b=(n/xun5*f5[xun5-1]%mod5+f5[n%xun5])%mod5;
    ll yu[3]={0,a,b};
    ll mod[3]={0,mod2,mod5};
    printf("%lld\n",CRT(yu,mod,2));
    return 0;
}

B 、Quadratic equation  (二次剩余)

题意:

(x+y)\equiv b\pmod p

(x\times y)\equiv c\pmod p

给出 p,b,c 计算 x,y

分析:

将上式化简得(x-y)^{2}=(x+y)^{2}-4xy 所以只要计算 (x-y)^{2}=b^{2}-4c(mod p) 

对n是否为mod p的二次剩余的判断,可以用欧拉准则。

然后直接套模板即可。

#include <bits/stdc++.h>

using namespace std;

const int mod=1e9+7;
typedef long long ll;

int k; 
ll a,p,w;

struct T{ll x,y;};

T mul_two(T a,T b,ll p){
    T ans;
    ans.x=(a.x*b.x%p+a.y*b.y%p*w%p)%p;
    ans.y=(a.x*b.y%p+a.y*b.x%p)%p;
    return ans;
}

T qpow_two(T a,ll n,ll p){
    T ans; ans.x=1; ans.y=0;
    while(n){
        if(n&1) ans=mul_two(ans,a,p);
        n>>=1;
        a=mul_two(a,a,p);
    }
    return ans;
}

ll qpow(ll a,ll n,ll p){
    ll ans=1; a%=p;
    while(n){
        if(n&1) ans=ans*a%p;
        n>>=1;a=a*a%p;
    }
    return ans%p;
}

ll Legendre(ll a,ll p){
    return qpow(a,(p-1)>>1,p);
}

int solve(ll n,ll p){
    n%=p; 
    if(p==2) return 1;
    if(Legendre(n,p)+1==p) return -1;
    else if(n==0) return 0;
    ll a=1;
    while(Legendre((a*a-n+p)%p,p)+1!=p)  a=rand()%p;
    T tmp; tmp.x=a; tmp.y=1; w=(a*a-n+p)%p;//W
    T ans=qpow_two(tmp,(p+1)>>1,p);
    return ans.x;
}

int main(){
    scanf("%d",&k);
    while(k--){
        ll b,c;
        scanf("%lld%lld",&b,&c);
        ll t=((b*b-c*4)%mod+mod)%mod;
        ll d=solve(t,mod);
        if(d==-1){
            printf("-1 -1\n");
            continue;
        }
        ll x=(b+d)*qpow(2ll,mod-2,mod)%mod,y=(b-x+mod)%mod;
        if(x>y) swap(x,y);
        printf("%lld %lld\n",x,y);
    }
    return 0;
}

D 、Knapsack Cryptosystem  (折半搜索)

题意:

给出n个数,最大是36,然后给出一个和,计算这些数中哪些数可以构成这个和,然后输出对应的二进制序列。

分析:

直接进行枚举范围太大,可以将数分为两半,然后枚举两边的所有和,存起来,用和减去左边的然后在右边进行查找即可。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

map<ll,int> ma1;
map<ll,int> ma2;

ll a[50];

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    int n,m;
    ll sum;
    scanf("%d%lld",&n,&sum);
    m=n>>1;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<(1<<m);i++){
        int tmp=i;
        ll res1=0,res2=0;
        for(int j=0;j<m;j++){
            if(tmp&(1<<j)) res1+=a[j+1],res2+=a[j+1+m];
        }
        ma1[res1]=tmp;
        ma2[res2]=tmp;
    }
    for(auto &x:ma1){
        ll res=x.fi;
        if(ma2[sum-res]){
            int tmp=ma1[res];
            int cnt=m;
            while(cnt--){
                if(tmp&1) printf("1");
                else printf("0");
                tmp>>=1;
            }
            cnt=m;
            tmp=ma2[sum-res];
            while(cnt--){
                if(tmp&1) printf("1");
                else printf("0");
                tmp>>=1;
            }
            printf("\n");
            break;
        }
    }
    return 0;
}

E 、All men are brothers  (并查集)

题意:

给出n个数字,刚开始每个数字在一个集合,每次操作后计算当前每个集合中取一个人一共取4个人的不同的情况数。

分析:

举个例子,用6个人来看,合并前后的情况(将1  6合并)为:

1 2 3 41 2 3 4
1 2 3 51 2 3 5
1 2 3 61 2 4 5
1 2 4 51 3 4 5

1 2 4 6 

2 3 4 5
1 3 4 5 
1 3 4 6 
1 3 5 6 
1 4 5 6 
2 3 4 5 
2 3 4 6 
2 3 5 6 
2 4 5 6 
3 4 5 6 
1 2 5 6 

 

那么明显可以发现的是减少的列就是左边既包含1又包含6的列。那么将这部分剪掉就好了。

剪掉的部分是他俩的乘积乘上其余任意两部分乘积的和,

直接计算其余各部分的任意两部分的和可能不好计算,那么我们考虑,事先计算好每两个的乘积然后每次进行维护就好了。

再列一个前后两两乘积的表就会很好的发现, 在这里就不咧了。

#include<bits/stdc++.h>

#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair<int,int>
#define pil pair<long long,long long>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;

const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;

const int maxn=1e5+10;

int f[maxn];
ll num[maxn];

int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}

int main(){
    #ifdef io
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif

    int n,m;
    scanf("%d%d",&n,&m);
    ll ans=1ll*n*(n-1)/4*(n-2)/3*(n-3)/2;
    printf("%lld\n",ans);
    for(int i=0;i<=n;i++){
        f[i]=i;num[i]=1ll;
    }
    ll sum=1ll*n*(n-1)/2;
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        int fa=find(a),fb=find(b);
        if(fa!=fb){
            int tot=n-num[fa]-num[fb];
            ans-=1ll*num[fa]*num[fb]*(sum-tot*num[fa]-tot*num[fb]-num[fa]*num[fb]);
            sum-=num[fa]*num[fb];
            f[fa]=fb;
            num[fb]+=num[fa];
        }
        printf("%lld\n",max(0ll,ans));
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值