牛客假日团队赛36(A数论,B置换群,D状压dp,E单调队列dp,I字典树上第k大,J、G区间dp)

题目链接

A-小凯的疑惑

题意:小凯手中有两种面值的金币,两种面值均为正整数且彼此互素。每种金币小凯都有无数个。在不找零的情况下,仅凭这两种金币,有些物品他是无法准确支付的。现在小凯想知道在无法准确支付的物品中,最贵的价值是多少金币?注意:输入数据保证存在小凯无法准确支付的商品。

乱猜了一个结论过了

正解请看:塞瓦韦斯特定理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll a,b;
    cin>>a>>b;
    if(a==1||b==1){
        printf("0");
        return 0;
    }
    cout<<a*b-a-b<<endl;
}

B-Reordering the Cows

题意:问你有多少个置换群,并且输出最大的置换群的size

水题了

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int a[N],b[N],vs1[N],vs2[N],fa[N],vis[N];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        vs1[a[i]]=i;
    }
    for(int i=1;i<=n;++i){
        cin>>b[i];
        vs2[b[i]]=i;
    }
    for(int i=1;i<=n;++i){
        fa[vs1[a[i]]]=vs2[a[i]];
    }
    int ans=0,t=0;
    for(int i=1;i<=n;++i){
        if(!vis[i]){
            int num=0;
            int id=i;
            while(vis[id]==0){
                vis[id]=1;
                num++;
                id=fa[id];
            }
            if(num>1) ans=max(ans,num),++t;
            //ans.push_back(num);
        }
    }
    if(ans==0) puts("0 -1");
    else
    printf("%d %d\n",t,ans);
}

D-Cow Decathlon

题意:约翰有 N 头奶牛,组成了一直队伍参加全能比赛。比赛一共有 N 项,每头奶牛必须参加一项比

赛,每项比赛也必须有一头奶牛参加。任何一头奶牛可以胜任任何一项比赛,但得分不一样。如果第

i 头奶牛参加第 j 项比赛,在比赛结束的时候,可以为团体总分增加 S i,j 。

比赛是按照顺序依次进行的。除了上述获得分数的方法之外,还有 B 种奖励分。获得奖励的方法

是在前几项比赛里获得足够的分数。具体来说,第 i 项奖励会在第 K i 项比赛结束的时候检查,如果

当时的总分大于或等于 P i ,奶牛们就可以立即获得额外的 A i 分。如果有多项奖励在同一时刻检查,

奶牛可以自由安排检查和加分的顺序。请问约翰应该如何安排奶牛参加比赛,才能让它们获得最高的

分数? 

做法:状压dp,由于考虑第几次比赛,所以这里的外层循环是状态i,内存循环是枚举某个牛,刚开始想歪了,卡样例卡半天

#include<bits/stdc++.h>
using namespace std;
const int N=23;
int dp[1<<N],n,m,len,s[N][N];
vector<pair<int,int> >G[N];
int get(int x)
{
    int ans=0;
    while(x)
    {
        if(x&1) ans++;
        x=x>>1;
    }
    return ans;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int k,p,v;
        cin>>k>>p>>v;
        G[k].push_back(make_pair(p,v));
    }
    for(int i=0;i<G[i].size();++i){
        sort(G[i].begin(),G[i].end());
    }

    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%d",&s[i][j]);

    len=(1<<n)-1;
    for(int i=0;i<=len;++i){
        int now=get(i);
        for(int j=1;j<=n;++j){

            if(i&(1<<(j-1))) continue;
            int id=i|(1<<(j-1));

            int val=dp[i]+s[j][now+1];
            for(auto it:G[now+1]){
                if(val>=it.first) val+=it.second;
            }
            dp[id]=max(dp[id],val);
        }
        //printf("i:%d dp:%d\n",i,dp[i]);
    }
    printf("%d\n",dp[len]);
}

E-跳房子

题意:跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:

在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。

现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g,但是需要注意的是,每次弹跳的距离至少为 1。具体而言,当g < d时,他的机器人每次可以选择向右弹跳的距离为 d-g, d-g+1, d-g+2,…,d+g-2,d+g-1,d+g;否则(当g ≥ d时),他的机器人每次可以选择向右弹跳的距离为 1,2,3,…,d+g-2,d+g-1,d+g。

现在小 R 希望获得至少 k 分,请问他至少要花多少金币来改造他的机器人。

做法:刚开始二分+线段树,于是超时,主要是每次二分就有新的点,那么我就要不断的二分离散化,导致常数很大,百度了一番代码,发现是二分+单调队列 维护dp。

被这题搞自闭了,于是正解代码没有自己打了:

代码参考:

#include<bits/stdc++.h>
#define cs const
#define re register
#define ll long long
#define fi first
#define se second
#define mp std::make_pair
#define pll std::pair<ll,ll>
using namespace std;
cs int N=5e5+10;
cs ll oo=1e18;
ll dis[N],val[N],dp[N],d,k;
int n;


inline bool check(ll x){
	ll mn=std::max(1ll,d-x),mx=d+x;
	std::deque<pll> Q;dp[0]=0;
	for(int re i=1;i<=n;++i) dp[i]=-oo;
	for(int re i=1,j=0;i<=n;++i){
		while(dis[j]<=dis[i]-mn){
			while(!Q.empty()&&Q.back().fi<dp[j]) Q.pop_back();
			Q.push_back(mp(dp[j],dis[j])),++j;
		}while(!Q.empty()&&Q.front().se<(dis[i]-mx)) Q.pop_front();
		dp[i]=(Q.empty()?-oo:Q.front().fi)+val[i];
		//printf("g:%lld i:%d dp:%lld\n",x,i,dp[i]);
		if(dp[i]>=k) return true;
	}
	return false;
}

int main(){
    cin>>n>>d>>k;

	for(int re i=1;i<=n;++i) cin>>dis[i]>>val[i];

	ll l=0,r=dis[n],ans=oo;
	while(l<=r){
		ll mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}printf("%lld\n",ans==oo?-1:ans);
}

/*
1 4 6
2 6





7 4 6
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
*/

F-Roadblock(gold)

题意:给你n节点m条边的图,你选择一条边 把这个权值变为两边,使得最短路最大;

做法:枚举最短路径上的边增大即可。

#include<bits/stdc++.h>

#define inf 0x3f3f3f3f
#define met(a,b) memset(a,b,sizeof a)
#define pb push_back
typedef long long ll;
using namespace std;
const int N = 1e3;
const int M = 24005;
int n,m,k;

int edg[N][N],d[N],pre[N];
void spfa(int x)
{
    met(d,inf);
    d[1]=0;
    queue<int>q;
    q.push(1);
    while(!q.empty()){
        int t=q.front();q.pop();
        for(int i=1;i<=n;i++){
            if(d[i]>d[t]+edg[t][i]){
                d[i]=d[t]+edg[t][i];
                pre[i]=t;
                q.push(i);
            }
        }
    }
}
int main()
{
    met(edg,inf);
    met(pre,-1);
    int ans1,ans2=0;
    scanf("%d%d",&n,&m);
    int u,v,w;
    while(m--){
        scanf("%d%d%d",&u,&v,&w);
        edg[u][v]=edg[v][u]=w;
    }
    spfa(0);
    ans1=d[n];
    for(int i=n;pre[i]!=-1;i--){
        int v=pre[i];
        edg[i][v]*=2;
        edg[v][i]*=2;
        spfa(1);
        ans2=max(ans2,d[n]);
        edg[i][v]/=2;
        edg[v][i]/=2;
    }
    printf("%d\n",ans2-ans1);
    return 0;
}

I-Auto-complete(silver)

题意:给你n个字符,m次询问,每次询问输入k  和字符s  问以s为前缀的第k大的字符是  n个中的第几个,按字典序排。

做法:建立字典树,然后树上找第k大即可。

这题容易写超空间,wa了很多才过了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char tr[N][27];
string s;
int num[N],tid[N],now[N];
int n,m,len,k,cnt;
int get(char c)
{
    return c-'a'+1;
}
void insert(int toid)
{
    int ch=0;
    for(int i=0;i<s.size();++i){
        int id=get(s[i]);
        if(tr[ch][id]==0) tr[ch][id]=++cnt;
        num[ch]++;
        ch=tr[ch][id];
    }
    num[ch]++;
    tid[ch]=toid;
}
int find(int now,int k){
	if(tid[now])k-=1;
	if(!k&&tid[now])return tid[now];

	for(int i=1;i<=26;i++){
        if(tr[now][i]){
            if(k>num[tr[now][i]]) k-=num[tr[now][i]];
            else return find(tr[now][i],k);
        }
	}
}
int qu()
{
    int ch=0;
    for(int i=0;i<s.size();++i){
        int id=get(s[i]);
        if(!tr[ch][id]) return -1;
        ch=tr[ch][id];
    }

    //printf("ch:%d k:%d num:%d\n",ch,k,num[ch]);
    if(num[ch]<k) return -1;
    else return find(ch,k);

}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;++i){cin>>s;insert(i);}


    for(int i=1;i<=m;++i){
        cin>>k>>s;
        printf("%d\n",qu());
    }
}

J-Secret Code

题意:

农民约翰收到一条的消息,记该消息为长度至少为2,只由大写字母组成的字符串S,他通过一系列操作对S进行加密。

他的操作为,删除S的前面1个或者后面1个个字符(但不删光整个S),并将剩下的部分连接到原字符串S的前面或者后面。如对于S=‘ABC’,共有8总可能的操作结果:

这题与G题的题意是,这里每次只能删除一个字符,而G题是可以删除前缀或者后缀,做法都差不多。

区间dp即可,具体文字描述看下面的G题,然后自己想一下改一改就可以了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10;
typedef long long ll;
const ll mod=2014;
ll dp[N][N],vis[N][N][N];
int n;
char s[N];
void add(ll &x,ll y)
{
    x=(x+y);
}
int main()
{
    cin>>s+1;
    n=strlen(s+1);
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;++j){
            for(int k=1;j+k-1<=n;++k){
                if(s[i+k-1]==s[j+k-1]) vis[i][j][k]=1;
                else break;
            }
        }
    }
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) dp[i][j]=1;


    //puts("****");

    for(int len=3;len<=n;len++){
        for(int i=1,j=i+len-1;j<=n;++i,++j){
            for(int k=i+1;k<=j;++k){
                int len1=k-1-i+1;
                int len2=j-k+1;

                if(len1<len2){
                    if(len1+1!=len2) continue;
                    if(vis[i][k][len1]){
                        add(dp[i][j],dp[k][j]);
                    }
                    if(vis[i][j-len1+1][len1]){
                        add(dp[i][j],dp[k][j]);
                    }
                }
                if(len1>len2){
                    if(len2+1!=len1) continue;
                    if(vis[i][k][len2]){
                        add(dp[i][j],dp[i][k-1]);
                    }
                    if(vis[k-1-len2+1][k][len2]){
                        add(dp[i][j],dp[i][k-1]);
                    }
                }


            }
        }
    }
    printf("%lld\n",dp[1][n]-1);
}

G-Secret Code(Silver)

题意:

农民约翰收到一条的消息,记该消息为长度至少为2,只由大写字母组成的字符串S,他通过一系列操作对S进行加密。

他的操作为,删除S的前面或者后面的若干个字符(但不删光整个S),并将剩下的部分连接到原字符串S的前面或者后面。如对于S=‘ABC’,共有8总可能的操作结果:

AABC

ABABC

BCABC

CABC

ABCA

ABCAB

ABCBC

ABCC

给出加密后的目标字符串,请计算共有多少种加密的方案。

对于同字符的字符串,加密方案不止一种,比如把AA加密成AAA,共有4种加密方案。将你的答案mod 2014后输出。

做法:

设dp[i][j]为区间[i,j]的方案数

观察题目我们发现一个新的字符串由两部分组成,一部分是原字符串,另一部分是原字符串的前缀或后缀

我们就枚举断点k更新方案数

初始化:

除了dp[1][len]外,都设为1,因为每个子串自身就可能成为一种方案,作为转移的基底

状态转移:

对于断点k,分为了[i,k],[k + 1,j]两段,由题另一部分不能与原串相等,故两段长度不能相等,而且长的那一段就一定是原串,若能判定出另一段是该段的前缀或后缀,就可以统计f[i][j] += f[i][k] 或 f[k + 1][j]

判定相等:

串的长度只有100,我们可以开一个macth[i][j][k]表示[i,i + k - 1]与[j,j + k - 1]是否相等,暴力预处理就好了

#include<bits/stdc++.h>
using namespace std;
const int N=1e2+10;
typedef long long ll;
const ll mod=2014;
ll dp[N][N],vis[N][N][N];
int n;
char s[N];
void add(ll &x,ll y)
{
    x=(x+y)%mod;
}
int main()
{
    cin>>s+1;
    n=strlen(s+1);
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;++j){
            for(int k=1;j+k-1<=n;++k){
                if(s[i+k-1]==s[j+k-1]) vis[i][j][k]=1;
                else break;
            }
        }
    }
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) dp[i][j]=1;
 
 
    //puts("****");
 
    for(int len=3;len<=n;len++){
        for(int i=1,j=i+len-1;j<=n;++i,++j){
            for(int k=i+1;k<=j;++k){
                int len1=k-1-i+1;
                int len2=j-k+1;
 
                if(len1<len2){
                    if(vis[i][k][len1]){
                        add(dp[i][j],dp[k][j]);
                    }
                    if(vis[i][j-len1+1][len1]){
                        add(dp[i][j],dp[k][j]);
                    }
                }
                if(len1>len2){
                    if(vis[i][k][len2]){
                        add(dp[i][j],dp[i][k-1]);
                    }
                    if(vis[k-1-len2+1][k][len2]){
                        add(dp[i][j],dp[i][k-1]);
                    }
                }
 
 
            }
        }
    }
    printf("%lld\n",dp[1][n]-1);
}

L-逛公园

不会写,参考来自 

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=100010;
const int M=200010;
const int K=51;
const int INF=1e9;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int w,to,nxt;
}e[2*M];
int n,m,k,p,cnt,head[N],dis[N],f[N][K];
bool vis[N],in[N][K];
struct cmp{
    bool operator ()(int a,int b){
        return dis[a]>dis[b];
    }
};
priority_queue<int,vector<int>,cmp>q;
inline void add(int u,int v,int w){
    e[++cnt].to=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
}
void bfs(int s){
    dis[s]=0;vis[s]=1;q.push(s);
    while(!q.empty()){
        int u=q.top();q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            if(i&1)continue;
            int v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!vis[v]){
                    vis[v]=1;q.push(v);
                }
            }
        }
    }
}
int dp(int u,int t){
    if(in[u][t])return f[u][t]=-1;
    if(f[u][t])return f[u][t];
    if(u==n)f[u][t]=1;
    in[u][t]=1;
    for(int i=head[u];i;i=e[i].nxt){
        if(!(i&1))continue;
        int v=e[i].to,w=e[i].w;
        int tmp=dis[v]+w-dis[u];
        if(tmp<=t){
            int tot=dp(v,t-tmp);
            if(tot==-1)return f[u][t]=-1;
            f[u][t]=(f[u][t]+tot)%p;
        }
    }
    in[u][t]=0;
    return f[u][t];
}
inline void init(){
    cnt=0;
    for(int i=1;i<=n;i++){
        dis[i]=INF,vis[i]=head[i]=0;
        for(int j=0;j<=k;j++)f[i][j]=in[i][j]=0;
    }
}
int main(){
    int t=read();
    while(t--){
        n=read(),m=read(),k=read(),p=read();
        init();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            add(u,v,w);add(v,u,w);
        }
        bfs(n);
        printf("%d\n",dp(1,k));
    }
    return 0;
}
发布了519 篇原创文章 · 获赞 69 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/105184285