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

A

签到题

/*简化版*/
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+10;


int main()
{
    int _;
    cin>>_;
    while(_--)
    {
        ll n;
        scanf("%lld",&n);
        ll t=n;
        while(t--) printf("%lld",n);
        puts("");
    }
}

B 求一个递推式

普通矩阵快速幂是二进制,这里可以化为十进制

化成十进制递推即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=2;
ll mod;
struct Matrix
{
	ll mat[MAXN][MAXN];
	Matrix() {}
	Matrix operator*(Matrix const &b)const
	{
		Matrix res;
		memset(res.mat, 0, sizeof(res.mat));
		for (int i = 0 ;i < MAXN; i++)
		for (int j = 0; j < MAXN; j++)
		for (int k = 0; k < MAXN; k++)
			res.mat[i][j] = (res.mat[i][j]+this->mat[i][k] * b.mat[k][j])%mod;
		return res;
	}
};

Matrix base,fi;
ll x0,x1,a,b;
int len;
const int N=1e6+10;
char s[N];
void gao() {
    int p = len;
    while (s[p] == '0') p--;
    s[p]--;
    for (int i = p + 1; i <= len; i++)
        s[i] = '9';
}
int main()
{
    cin>>x0>>x1>>a>>b;
    cin>>s+1>>mod;
	base.mat[0][0] = a;
	base.mat[0][1] = b;
	base.mat[1][0] = 1;
	base.mat[1][1] = 0;
	fi.mat[0][0]=x1;
	fi.mat[1][0]=x0;
    len=strlen(s+1);
    gao();
	Matrix res;
	memset(res.mat, 0, sizeof(res.mat));
	for (int i = 0; i < MAXN; i++)
		res.mat[i][i] = 1;

	for(int i=len;i>=1;--i)
	{
	    if(s[i]>'0')
        {
            int d=s[i]-'0';
            while(d--) res = res*base;
        }
        int d=9;
        Matrix tmp=base;
        while(d--) base = base*tmp;
	}

    res=res*fi;
    printf("%lld\n", res.mat[0][0]);

	return 0;
}

C generator 2 BSGS

什么是bsgs?

BSGS一般是求下面这个公式:a^{n}\equiv b\%p,a、b、p已知,求n。

将n分解为x\sqrt{p}-y那么原公式可以化简为a^{x*\sqrt{p}}\equiv b*a^{y},模数已经省略。此时我们可以枚举x从1到sqrt(p)用一个map保存左边的数。

接着枚举y从1到sqrt(p),把右边的值算出来,从map里查找即可。

那么这个题怎么用bsgs呢?

 

题目公式:x_{i}=a*x_{i-1}+b.

求前几项可以得到

x_{0}=x0,x_{1}=a*x_{0}+b,x2=a*a*x_{0}+a*b+b,x3=a^{3}*x_{0}+a*a*b+a*b+b

得到一般公式x_{n}=a^{n}*x_{0}+\frac{b*(1-a^{n})}{1-a},后面这一项就是等比公式的求和公式

继续化简成a^{n}\equiv b mod p这种形式。

a^{n}=\frac{x_{n}*(1-a)-b}{x_{0}*(1-a)-b}

右边这一堆当成一个变量v就可以了

a^{n}\equiv b \%p

注意这里的n得这样化简:n=x*1000-y,原因不详。因为学习来自:https://blog.csdn.net/ccsu_cat/article/details/98184813#commentBox

那么得到a^{x*1000}\equiv b*a^y,枚举x从1到1e6预处理一下,接着每次询问,暴力枚举y就可以得到答案了。

还得注意的是,a=0,a=1的情况需要特殊处理。。

自己的代码一直超时,

【自己的代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int xn,x0,a,b,p;
ll n;
ll powmod(ll a,ll b) {ll res=1;a%=p;
for(;b;b>>=1){if(b&1)res=1ll*res*a%p;a=1ll*a*a%p;}return res%p;}
unordered_map<int,int>mp;
const int t1=1000;
ll ans;
int q,inv,txt,tmp,y;
int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        scanf("%lld%d%d%d%d",&n,&x0,&a,&b,&p);
        mp.clear();
        tmp=1;
        y=powmod(a,t1);
        for(int x=1;x<=1e6;++x)
        {
             tmp=1ll*y*tmp%p;
            if(!mp.count(tmp)) mp[tmp]=x*t1;
        }
        scanf("%d",&q);
        inv=powmod((1ll*x0*(1-a)-b)%p,(p-2));
        txt=powmod(b,p-2);
        while(q--)
        {
            scanf("%d",&xn);
            ans=0x3f3f3f3f3f3f3f3f;
            if(a==0)
            {
                //xn=b  x0=x0
                if(xn==x0) printf("0\n");
                else if(xn==b) printf("1\n");
                else printf("-1\n");
                continue;
            }
            else if(a==1)
            {
                //xn-x0/b   xn=x0+b
                ans=1ll*(xn-x0+p)*txt%p;
                if(ans<n) printf("%lld\n",ans);
                else printf("-1\n");
                continue;
            }
            tmp=a;
            int V=a;
            for(int t2=0;t2<=1000;++t2)
            {
                if(mp[V]) ans=min(ans,1ll*mp[V]-t2);
                V=(1ll*xn*(1-a)-b)%p*inv%p*tmp%p;
                tmp=1ll*tmp*a%p;
            }
            if(ans==0x3f3f3f3f3f3f3f3f||ans>=n) printf("-1\n");
            else printf("%lld\n",ans);
        }
    }
    return 0;
}

【别人的AC代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int mod;
int t1=1001,t2=1e6;
ll powmod(ll a,ll b) {ll res=1;a%=mod;
assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
unordered_map<int,int>mp;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        mp.clear();
        ll n;int x0,a,b,Q;
        scanf("%lld%d%d%d%d%d",&n,&x0,&a,&b,&mod,&Q);
        ll s = powmod(a,t1);
        int x = 1;
        for(int i=1;i<=t2;++i){
            x = 1ll*x*s%mod;
            if(!mp.count(x)) mp[x] = i*t1;
        }
        int mu = (b+1ll*a*x0%mod-x0+mod)%mod;
        int inv = powmod(mu,mod-2);
        while(Q--){
            ll v;
            scanf("%lld",&v);
            if(!a){
                if(v==x0) puts("0");
                else if(v==b) puts("1");
                else puts("-1");
                continue;
            }
            if(a==1){
                ll ans = (v-x0+mod)%mod*(powmod(b,mod-2))%mod;
                if(ans<n) printf("%lld\n",ans);
                else puts("-1");
                continue;
            }
            int ans=mod+1;
            v = (1ll*v*(a-1)%mod+b)*inv%mod;
            int y=v;
            for(int i=0;i<=t1;++i){
                if(mp.count(y)) ans=min(ans,mp[y]-i);
                y=1ll*y*a%mod;
            }
            if(ans==mod+1||1ll*ans>=n) puts("-1");
            else printf("%d\n",ans);
        }
    }
}

G dp

统计s串中比t串大的子序列个数。

做法:设dp[i][j]为前i位选取j个数时的合法个数,f[i][j]为前i个数,选取j个数刚好等于t串时的个数。

二维枚举一下,s串和t串,若s[i]>t[j],那么d[i][j]=d[i-1][j-1]+d[i-1][j]+f[i-1][j-1]

解析一下:当前数可取,由上一层合法且少一个数的d[i-1][j-1]转移一次,以及从上一层相等的且少一个数的f[i-1][j-1]转移一次,以及上层已有合法d[i-1][j]转移一次

若若s[i]==t[j]那么f[i][j]+=f[i-1][j-1],d[i][j]=d[i-1][j-1]+d[i-1][j]同理

只能由上一层合法且少一个数的d[i-1][j-1]转移一次,以及上层已有合法d[i-1][j]转移一次

初始值f[i][0]=1

【代码】

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e3+10;
ll c[N][N];
char s[N],t[N];
ll dp[N][N],f[N][N];
const ll mod=998244353;
void init(){
    c[0][0] = 1;
    for (int i = 1; i <= 3000; i++) {
        c[i][0] = c[i][i] = 1;
        for (int j = 1; j < i; j++)
            c[i][j]=(c[i - 1][j - 1] + c[i - 1][j])%mod;
    }
}
int main()
{

    init();
    int _;cin>>_;
    while(_--)
    {
        int n,m;
        cin>>n>>m;
        scanf("%s%s",s+1,t+1);
        for(int i=0;i<=n+1;++i)
        for(int j=0;j<=m+1;++j) dp[i][j]=f[i][j]=0;
        for(int i=0;i<=n;++i) f[i][0]=1;

        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            dp[i][j]=(dp[i][j]+dp[i-1][j])%mod,
            f[i][j]=f[i-1][j],
            dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod;
            for(int j=1;j<=min(i,m);++j)
            {
                if(s[i]>t[j]) dp[i][j]=(dp[i][j]+f[i-1][j-1])%mod;
                else if(s[i]==t[j]) f[i][j]=(f[i][j]+f[i-1][j-1])%mod;
            }
        }
        ll ans=0;
        for(int i=1;i<=n;++i)
        {
            if(s[i]!='0')
            for(int j=m;j<=n-i;++j)
            {
                ans=(ans+c[n-i][j])%mod;
            }
        }
        printf("%lld\n",(dp[n][m]+ans)%mod);
    }
    return 0;
}
/*
2 2
24
13
*/

H题

询问字符串,问能否构造这么一个字符串。

拓扑排序的经典问题了。当时不会写~~丢人

【代码】

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10,M=100;
vector<int>G[N],val[N];
char s[N],t[N];
map<int,char>ma;
vector<int>a;
vector<char>ans;
int vis[30],vs[N],du[N],n,m;
void tobo()
{
    int res=0;
    int tt=n;
    while(1)
    {
        int tmp=-1;
        for(int i=0;i<a.size();++i)
        {
            if(du[a[i]]==0){
                tmp=a[i];
                break;
            }
        }
        if(tmp==-1) break;
        ans.push_back(ma[tmp]);
        res++;
        du[tmp]=-1;
        for(int i=0;i<G[tmp].size();++i) du[G[tmp][i]]--;
    }
    if(ans.size()==n)
        for(int i=0;i<ans.size();++i) printf("%c",ans[i]);
    else printf("-1");
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m*(m-1)/2;++i)
    {
        int q;
        cin>>s+1>>q; if(q>0) cin>>t+1;
        memset(vis,0,sizeof(vis));
        int pre=-1;
        for(int j=1;j<=q;++j)
        {
            vis[t[j]-'a']++;
            int num=vis[t[j]-'a'];
            int cnt=num*M+t[j]-'a';
            ma[cnt]=t[j];
            a.push_back(cnt);
            if(pre==-1) {
                pre=cnt;continue;
            }
            du[cnt]++;
            G[pre].push_back(cnt);
            pre=cnt;
        }
    }
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end());
    if(a.size()!=n) printf("-1");
    else tobo();
    return 0;
}

F 题神仙题

先献上题解的说法:

求最大独立集数量会,可这个输出这个集合是什么操作?

拿别人代码欣赏欣赏,一天过去了,还是没看懂,先贴上当板子用吧(凄惨~~~)一天只补一个题的凄惨。。。。。

写了一些解析

【代码】

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
const int mod=3007;
int a[maxn];
int match[maxn],match2[maxn];
bool mp[maxn][maxn];
vector<int>V;
int vis[maxn];
int mark[maxn];
int n;
struct Edge{
    int from,nex,to;
}edge[maxn*2005];
int head[maxn],cnt;
inline void addedge(int u,int v){
    edge[cnt]={u,head[u],v};
    head[u]=cnt++;
}
bool dfs(int u){//匹配点
    vis[u]=1;
    for(int i=head[u];~i;i=edge[i].nex){
        int v=edge[i].to;
        if(!vis[v]){
            vis[v]=true;
            if(match[v]==-1||dfs(match[v])){
                match[v]=u;
                match2[u]=v;//被匹配过的点
                return true;
            }
        }
    }
    return false;
}
void dfs2(int u){//染色
    for(int i=head[u];~i;i=edge[i].nex){
        int v=edge[i].to;
        if(mark[v]!=-1)continue;
        mark[v]=mark[u]^1;
        dfs2(v);
    }
}
int cal(int x,int y)
{
    int num=0;
    while(x||y)
    {
        if(x%2!=y%2) num++;
        x>>=1,y>>=1;
    }
    return num;
}
int main()
{
    memset(head,-1,sizeof head);
    ios::sync_with_stdio(false);
    for(int i=0;i<=30;i++){
        V.push_back(1<<i);
    }
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    sort(a+1,a+n+1);
   // memset(viss,-1,sizeof viss);
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
        	/*int num=cal(a[i],a[j]);//这种写法会超时,亲身测试 
			if(num==1)
			{
				addedge(i,j);
                addedge(j,i);
			 } */
            if(*lower_bound(V.begin(),V.end(),a[i]^a[j])==(a[i]^a[j])){
                addedge(i,j);
                addedge(j,i);
            }
        }
    }
    memset(mark,-1,sizeof mark);
    for(int i=1;i<=n;i++){//染色操作,分成两部分,方便接下来的操作
	//不用输出路径的话无需染色操作 
        if(mark[i]==-1){
            mark[i]=0;
            dfs2(i);
        }
    }
    int sum=0;
    memset(match,-1,sizeof match);
    for(int i=1;i<=n;i++){//进行最大匹配操作,并记录了匹配的方案
	//match2记录mark为0,的 match2记录mark为1的 
        if(mark[i]!=0)continue;
        memset(vis,0,sizeof vis);
        if(dfs(i)) sum++;//0在左边出发找匹配点,从0一边出发找
    }
    cout<<n-sum<<endl;//最大团的数量等于所有的点减去最大匹配数 
    memset(vis,0,sizeof vis);
    for(int i=1;i<=n;i++){
        if(mark[i]==0&&(!match2[i])){//在左边为0,且未匹配 
            //???这里就看不懂了
			//最后来一遍dfs二分图匹配什么意思 
            dfs(i);
        }
    }
    for(int i=1;i<=n;i++){
        if((mark[i]==0&&vis[i]==0)||(mark[i]==1&&vis[i]==1))continue;
        ///这样的输出方式也是不懂是什么鬼什么鬼么鬼鬼(暴怒!!) 
        cout<<a[i]<<" ";
 
    }
    cout<<endl;
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙大学ccsu_deer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值