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

目录

A 、digits 2  (简单构造)

B generator 1  (十进制快速幂)

 C、 generator 2  (BSGS)

 E、 independent set 1  (状压DP)

 F、 maximum clique 1 (最大团)

G 、subsequence 1  (DP+组合计数)

H 、subsequence 2  (拓扑排序)

I 、three points 1  (计算几何)


A 、digits 2  (简单构造)

太简单就不写了。

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

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

    int T;
    scanf("%d",&T);
    while(T--){
        int a;
        scanf("%d",&a);
        for(int i=0;i<a;i++){
            printf("%d",a);
        }
        printf("\n");
    }
    return 0;
}

B generator 1  (十进制快速幂)

题意:

给出然后给出一个n,n的范围是\tiny 10^{10^6},然后计算\tiny x_{n}

分析:

因为是个矩阵,所以没办法用欧拉降幂,但是又恐于数太大,直接转化二进制快速幂肯定超时,所以先进行十进制拆分,模拟而二进制进行计算。

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

typedef struct martix{
    ll a[2][2];
    martix(){
        mm(a,0);
        a[0][0]=a[1][1]=1;
    }
}mat;

mat mul(mat a,mat b,ll mod){
    mat res;
    rep(i,0,1){
        rep(j,0,1){
            res.a[i][j]=0;
        }
    }
    rep(i,0,1){
        rep(j,0,1){
            rep(k,0,1){
                res.a[i][j]+=a.a[i][k]*b.a[k][j]%mod;
                res.a[i][j]%=mod;
            }
        }
    }
    return res;
}

mat quick_two(mat a,ll b,ll mod){
    mat res;
    while(b){
        if(b&1) res=mul(res,a,mod);
        a=mul(a,a,mod);
        b>>=1;
    }
    return res;
}

mat quick_ten(mat a,char b[],ll mod){
    mat res;
    int len=strlen(b);
    for(int i=len-1;i>=0;i--){
        if(b[i]!='0') res=mul(res,quick_two(a,b[i]-'0',mod),mod);
        a=quick_two(a,10ll,mod);
    }
    return res;
}

char n[1000006];

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

    ll x0,x1,a,b;
    ll mod;
    scanf("%lld%lld%lld%lld%s%lld",&x0,&x1,&a,&b,n,&mod);
    int len=strlen(n);
    if(n[len-1]!='0') n[len-1]-=1;
    else{
        len--;
        while(n[len]=='0'){
            n[len]='9';
            len--;
        }
        n[len]--;
    }
    mat tmp;
    tmp.a[0][0]=a;tmp.a[0][1]=b,tmp.a[1][0]=1ll,tmp.a[1][1]=0;
    mat res=quick_ten(tmp,n,mod);
    ll ans=(x1*res.a[0][0]%mod+x0*res.a[0][1]%mod)%mod;
    printf("%lld\n",ans);
    return 0;
}

 C、 generator 2  (BSGS)

题意:

计算满足条件的最小的i,给出v,使得等号右边同余v

分析:

将上述等式进行化简就可以看出来是BSGS了

但是可能会TLE(理论不会啊感觉)

所以分快再分的小点,就分1000一块吧,可是还有点卡,同一份代码有时候能过有时候不能过。

通过这题我发现int比longlong快,puts比printf快

#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++)
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;

int x0,a,b,mod;
ll n;
int t1=1001,t2=1e6;

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

inline ll inv(ll a){
    return quick_pow(a,mod-2)%mod;
}

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

    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%d%d%d%d",&n,&x0,&a,&b,&mod);
        int Q;
        unordered_map<int,int> up;
        scanf("%d",&Q);
        ll p=quick_pow(a,t1);
        int tmp=1;
        for(int i=1;i<=t2;i++){
            tmp=1ll*tmp*p%mod;
            if(!up.count(tmp))
                up[tmp]=t1*i;
        }
        int ni=inv((1ll*(a-1)*x0%mod+b+mod)%mod);
        while(Q--){
            ll v;
            scanf("%lld",&v);
            if(a==0){
                if(v==x0) puts("0");
                else if(v==b) puts("1");
                else puts("-1");
                continue;
            }else if(a==1){
                ll ans=1ll*(v-x0+mod)%mod*inv(b)%mod;
                if(ans<n) printf("%lld\n",ans);
                else puts("-1");
                continue;
            }else{
                v=(v*(a-1)%mod+b)%mod*ni%mod;
                int ans=mod+1;
                int tmp3=v;
                for(int i=0;i<=t1;i++){
                    if(up.count(tmp3)){
                        ans=min(ans,up[tmp3]-i);
                    }
                    tmp3=1ll*tmp3*a%mod;
                }
                if(1ll*ans>=n||ans==mod+1) puts("-1");
                else printf("%d\n",ans);
            }
        }

    }
    return 0;
}

 E、 independent set 1  (状压DP)

题意:

给出一个无向无环图,计算这个图的所有子图的最大独立子集之和。

分析:

因为n只有26,所以考虑位压缩进行计算。

该点连的边用二进制位存储起来。

固定左端点,对后面的二进制位依次枚举即可。注意要减去本身包含的边。

注意内存,所以采用char进行存储。

#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++)
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;

int g[26];
char dp[(1<<26)];
int n,m;
int ans=0;

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

    scanf("%d%d",&n,&m);
    while(m--){
        int u,v;
        scanf("%d%d",&u,&v);
        g[u]|=(1<<v);
        g[v]|=(1<<u);
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<(1<<i);j++){
            dp[j|(1<<i)]=max(dp[j],(char)(dp[j-(j&g[i])]+1));
        }
    }
    for(int i=0;i<(1<<n);i++){
        ans+=dp[i];
    }
    printf("%d\n",ans);
    return 0;
}

 F、 maximum clique 1 (最大团)

题意:

给出一个序列,输出最大的子集其中两两的二进制位至少有两位不同。

分析:

首先对于最大团的题目,要先想到是转换成最大独立子集来考虑(因为补图的最大独立子集即为原图的最大团)

最大团(图中最大的完全子图)最大独立子集(最大的点集其中两两没有边相连)

定理:最大独立子集等于n-最大匹配。

考虑这个题:将题目条件转化为补集的条件即为计算恰有一个二进制位相同的数。

那么我们可以利用这个性质进行建图计算最大匹配数即可。

题目还有个要求是输出最大独立子集。(这块我也看了别人代码好久才有点理解)

首先考虑几点:

1、肯定是二进制位数是奇数和二进制位数为偶数的有边相连。

2、如果二进制是偶数的数多的话,那么对于偶数肯定是s=1,t=0,对于奇数正好相反。

3、如果二进制是奇数的数多的话,那么对于偶数数肯定是s=0,t=1,对于奇数正好相反。

那么根据这三点就可以判断出最大独立子集了。

(以上想法不是很成熟,准备补完多校再刷下这快的专题,若有不对希望大佬批评指正。)

#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=5005;

int a[maxn];
int match[maxn];
bool s[maxn];
bool t[maxn];
bool b[maxn];
vector<int> vec[maxn];

bool dfs(int u){
    s[u]=1;
    for(auto v:vec[u]){
        if(t[v]==0){
            t[v]=1;
            if(match[v]==0||dfs(match[v])){
                match[v]=u;
                return 1;
            }
        }
    }
    return 0;
}

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

    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    rep(i,1,n){
        rep(j,i+1,n){
            if(__builtin_popcount(a[i]^a[j])==1) {
                vec[i].push_back(j);
                vec[j].push_back(i);
            }
        }
    }
    mm(match,0);
    for(int i=1;i<=n;i++){
        rep(j,1,n) t[j]=0;
        if(dfs(i)) b[i]=1;
    }
    mm(s,0);
    mm(t,0);
    for(int i=1;i<=n;i++){
        if(!b[i]) dfs(i);
    }
    vector<int> ans;
    for(int i=1;i<=n;i++)
        if(__builtin_parity(a[i])==1&&s[i])ans.push_back(i);
    for(int i=1;i<=n;i++)
        if(__builtin_parity(a[i])==0&&!t[i])ans.push_back(i);
    printf("%d\n",ans.size());
    for(auto x:ans){
        printf("%d ",a[x]);
    }
    return 0;
}

G 、subsequence 1  (DP+组合计数)

题意:

给出两个数字组成的字符串,计算第一个字符串当中有多少个子序列大于第二个字符串,不能有前导零。

分析:

对于长度大于第二个序列的直接用组合数计算即可,那么考虑长度相同的子序列的情况,因为前缀可能出现相同的情况,所以这部分用DP进行递推计算;

\tiny dp[i][j] 表示第一个字符串的前i位有多少个子序列与第二个字符串的前j位相同。

那么很容易得到递推式:

\tiny if(s[i]!=t[j])    \tiny dp[i][j]=dp[i-1][j]

\tiny else                        \tiny dp[i][j]=dp[i-1][j]+dp[i-1][j-1]

#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 ll mod=998244353;

const int maxn=3005;

ll c[maxn][maxn];
ll dp[maxn][maxn];
char s[maxn],t[maxn];

void init(){
    c[0][0]=1ll;
    for(int i=0;i<maxn;i++){
        c[i][0]=c[i][i]=1ll;
    }
    for(int i=1;i<maxn;i++){
        for(int j=1;j<i;j++){
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
        }
    }
}

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

    init();
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d%d",&n,&m);
        scanf("%s%s",s+1,t+1);
        ll ans=0;
        for(int i=0;i<=n;i++){
            dp[i][0]=1;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=min(i,m);j++){
                dp[i][j]=dp[i-1][j];
                if(s[i]==t[j]) dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod;
                if(s[i]>t[j]) ans=(ans+dp[i-1][j-1]*c[n-i][m-j]%mod)%mod;
            }
        }
        for(int i=1;i<=n;i++){
            if(s[i]=='0') continue;
            for(int j=m;j<=n-i;j++){
                (ans+=c[n-i][j])%=mod;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

H 、subsequence 2  (拓扑排序)

题意:

给出一个字符串,然后再给出一个原串删除一些字符后的字符串,然后给出这么一系列的字符串,求原串是什么。

分析:

没想到是拓扑排序,不过看了题解后也感觉很顺其自然是拓扑排序。

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

int in[28*maxn];
int num[30];
vector<int> g[28*maxn];

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

    int n,m;
    cin>>n>>m;
    for(int i=0;i<m*(m-1)/2;i++){
        string s,t;
        int len;
        cin>>s>>len;
        if(!len){
            continue;
        }
        cin>>t;
        int a=0,b=0,cur=0,now;
        for(int i=0;i<len;i++){
            if(t[i]==s[0]){
                a++;
                now=(t[i]-'a')*maxn+a;
            }else if(t[i]==s[1]){
                b++;
                now=(t[i]-'a')*maxn+b;
            }
            if(cur){
                in[now]++;
                g[cur].pb(now);
            }
            cur=now;
        }
        num[s[0]-'a']=a;
        num[s[1]-'a']=b;
    }
    queue<int> q;
    for(int i=0;i<26;i++){
        if(num[i]&&in[i*maxn+1]==0){
            q.push(i*maxn+1);
        }
    }
    string ans;
    while(!q.empty()){
        int fro=q.front();q.pop();
        ans.push_back((fro-1)/maxn+'a');
        for(auto x:g[fro]){
            in[x]--;
            if(in[x]==0) q.push(x);
        }
    }
    if(ans.size()!=n ) cout<<"-1"<<endl;
    else cout<<ans<<endl;
    return 0;
}

I 、three points 1  (计算几何)

题意:

给出三角形的三条边,拼成一个三角形并且这个三角形的三个点的坐标都在给的矩形内。

分析:

因为如果三角形能放下矩形内,那么一定可以经过平移然后三角的点在矩形的顶点上,那么可以枚举每个三角形的形态,进行判断即可。

#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++)
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 double eps=1e-8;

double w,h;
pid p[5];

int cmp(double x){
    if(fabs(x)<eps) return 0;
    if(x>0) return 1;
    return -1;
}

bool judge(double a,double b,double c,int o,int t,int s){
    p[o]={0.0,0.0};
    double ang=acos(1.0*(a*a+c*c-b*b)/(2.0*a*c));
    if(a<=w) p[t]={a,0.0};
    else p[t]={w,sqrt(1.0*a*a-w*w)},ang+=acos(1.0*w/a);
    p[s]={c*cos(ang),c*sin(ang)};
    if(cmp(p[t].fi-w)<=0&&cmp(p[t].se-h)<=0&&cmp(p[s].fi-w)<=0&&cmp(p[s].se-h)<=0&&cmp(p[s].fi)>=0&&cmp(p[s].se)>=0){
        printf("%.12f %.12f %.12f %.12f %.12f %.12f\n",p[1].fi,p[1].se,p[2].fi,p[2].se,p[3].fi,p[3].se);
        return 1;
    }
    return 0;
}

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

    double a,b,c;
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lf%lf%lf%lf%lf",&w,&h,&a,&b,&c);
        if(judge(a,c,b,1,2,3))continue;
        if(judge(b,c,a,1,3,2))continue;
        if(judge(c,b,a,2,3,1))continue;
        if(judge(a,b,c,2,1,3))continue;
        if(judge(c,a,b,3,2,1))continue;
        if(judge(b,a,c,3,1,2))continue;
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值