6.28集训--集训模拟赛2

总结

6.28集训--集训模拟赛2_第1张图片
第一题:n只有4,直接暴力
第二题:Tarjan缩点之后跑一个最长路
第三题:DP
第四题:思维量较大

A、翻转游戏

题目描述

6.28集训--集训模拟赛2_第2张图片
6.28集训--集训模拟赛2_第3张图片

分析

\(n\)的范围很小,所以我们考虑状压DP
我们设\(f[i][j][k][m]\)为第一行的状态为\(i\),第二行的状态为\(j\),第三行的状态为\(k\),第四题的状态为\(m\)所需要的最小步数
所以我们暴力枚举6重循环,时间复杂度为\(15 \times 15 \times 15 \times 15 \times 4 \times 4\)

考场95分代码

#include
using namespace std;
const int maxn=5;
int f[1<

100分代码

因为我们的状态不是从0开始的,所以我们不能只从状态为0正着开始枚举
比如下面这一组数据

bbbb
bwwb
bwwb
bbbb

正解应该输出4,但是上面的代码却输出6
解决这一个问题,我们倒着枚举一遍就可以了

#include
using namespace std;
const int maxn=5;
int f[1<=0;i--){
        for(int j=(1<<4)-1;j>=0;j--){
            for(int k=(1<<4)-1;k>=0;k--){
                for(int m=(1<<4)-1;m>=0;m--){
                    for(int d=1;d<=4;d++){
                        if(d==1){
                            for(int n=1;n<=4;n++){
                                if(n==1){
                                    int now=i^(1<<(n-1));
                                    now=now^(1<

B、抢掠计划

题目描述

6.28集训--集训模拟赛2_第4张图片
6.28集训--集训模拟赛2_第5张图片
6.28集训--集训模拟赛2_第6张图片
6.28集训--集训模拟赛2_第7张图片

分析

显然,处于同一个强连通分量的点可以互相到达
所以我们将每一个强连通分量分别缩点
缩完点后的图就变成了一个有向无环图
这时,我们就可以用SPFA求出最长路

100分代码

#include
using namespace std;
typedef long long ll;
const int maxn=2e6+5;
int head[maxn],tot=1;
struct asd{
    int from,to,next;
    ll val;
}b[maxn],b2[maxn];
void ad(int aa,int bb,ll cc){
    b[tot].from=aa;
    b[tot].to=bb;
    b[tot].next=head[aa];
    b[tot].val=cc;
    head[aa]=tot++;
}
int h2[maxn],t2=1;
void ad2(int aa,int bb){
    b2[t2].from=aa;
    b2[t2].to=bb;
    b2[t2].next=h2[aa];
    h2[aa]=t2++;
}
int dfn[maxn],low[maxn],dfnc,sta[maxn],top;
int js,shuyu[maxn],siz[maxn];
ll a[maxn],ans[maxn];
void tar(int xx){
    dfn[xx]=low[xx]=++dfnc;
    sta[++top]=xx;
    for(int i=h2[xx];i!=-1;i=b2[i].next){
        int u=b2[i].to;
        if(!dfn[u]){
            tar(u);
            low[xx]=min(low[xx],low[u]);
        } else if(!shuyu[u]){
            low[xx]=min(low[xx],dfn[u]);
        }
    }
    if(dfn[xx]==low[xx]){
        js++;
        while(sta[top]!=xx){
            int y=sta[top--];
            shuyu[y]=js;
            siz[js]++;
        }
        top--;
        shuyu[xx]=js;
        siz[js]++;
    }
}
queue q;
int vis[maxn];
ll dis[maxn];
void SPFA(int xx){
    q.push(xx);
    vis[xx]=1;
    while(!q.empty()){
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=b[i].next){
            int u=b[i].to;
            if(dis[u]

C、测绘

题目描述

6.28集训--集训模拟赛2_第8张图片
6.28集训--集训模拟赛2_第9张图片

分析

这一道题非常显然是一道DP题
我们设\(f[i][j]\)为前\(i\)个数中已经选择了\(j\)个数的价值,并且处于\(i\)位置上的数一定选择
那么就有$ f[i][j]= \min (f[i][j],f[k][j-1]+sum[k][i]);\( 其中\)j-1 \leq k < i\( \)sum[i][j]\(为选走\)i,j\(位置上的数,区间\)[i,j]\(中的数产生的误差 这一道题要注意初始化和最后的处理 我们需要将\)f[i][1]\(和\)f[i][i]\(预处理一下 同时,当\)DP$结束后,因为最右边的价值没有计算,所以我们需要把最右边的价值加上

100分代码

#include
using namespace std;
typedef long long ll;
const int maxn=105;
int n,vis[maxn],jud;
ll e,a[maxn],sum[maxn][maxn],f[maxn][maxn];
int main(){
    memset(f,0x3f,sizeof(f));
    scanf("%d%lld",&n,&e);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            for(int k=i+1;k

D、奖学金

6.28集训--集训模拟赛2_第10张图片
6.28集训--集训模拟赛2_第11张图片
6.28集训--集训模拟赛2_第12张图片

分析

数据范围高达 10 万,显然至少是\(O(n∗log(n))\) 才能通过。
我们分析性质中位数 \(ai\) 必须满足:\(n2+1≤i≤C−n2\)
\(i=n2+1\) 时,我们必须选上最小分数最低的前 \(n2\) 的猪。
所以我们可以枚举每一个中位数,用一个维护奖金的大根堆,每枚举完一个中位数,如果当前的奖金比堆顶的小,则交换,始终保证堆的有 \(n2\) 个数,同时用一个数组 \(f[i]\) 维护如果选 \(ai\) 为中位数,前 \(n2\) 个数的最小奖金。
同上,倒序维护,求出 \(g[i]\) 表示,如果选 \(ai\) 为中位数,则后 \(n2\) 个数最小奖金。
显然答案为满足 \(f[i]+g[i]+a[i].w<=F\) 的最大的 \(a[i].s\)

代码

#include 
const int maxn=2e5+5;
int n,c,F;
std::priority_queue  q;
struct Node{
    int s,w;//分数,奖金
} a[maxn];
bool cmp(const Node &a, const Node &b){
    return a.sa[i].w){//如果当前的奖金小于堆顶则交换掉
            q.pop();
            sum-=top;
            sum+=a[i].w;
            q.push(a[i].w);
        }
    }

    sum=0;
    while(!q.empty()) q.pop();
    for(int i=c;i>=c-n/2+1;--i){//成绩最高的n/2进入队列
        sum+=a[i].w;
        q.push(a[i].w);
    }
    //g[i]:表示以i为中位数后n/2人的最低奖金
    for(int i=c-n/2;i>=1;--i){
        g[i]=sum;
        int top=q.top();
        if(top>a[i].w){//交换
            q.pop();
            sum-=top;
            sum+=a[i].w;
            q.push(a[i].w);
        }
    }
    //中位数的取值范围是[n/2+1,c-n/2]
    //因为要求最大中位数,所以倒序
    for(int i=c-n/2;i>=n/2+1;--i)
        if(a[i].w+f[i]+g[i]<=F){
            printf("%d", a[i].s);
            return;
        }
    printf("-1\n");
}
int main(){
    Init();
    Solve();
    return 0;
}

你可能感兴趣的:(6.28集训--集训模拟赛2)