【考试反思】联赛模拟测试9

凯爹怒 (mathbb{AK}) 钛聚辣

集训很长时间了,一直以为没有怎么挂分,结果今天直接打脸,再加上两道原题,人直接爆炸。

是状态的问题吗?不,就是菜吧。

T1: 60 ( ightarrow) 20

T2: 60 ( ightarrow) 15

T3: 50 ( ightarrow) 30

T4: 100 ( ightarrow) 20

T1:嚎叫响彻在贪婪的厂房

考场上没想到用 set然后 (O(n^2log n)) 暴力排序的,莫名其妙挂了。


根据题目名字的提示我们可以想到贪心。

可以想到不合法的情况就是 (gcd=1) 或相等,其他直接和前面合并即可。利用 set 做到 (O(nlog n))

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,ans;
int a[maxn];
set<int> s;

inline int read(){
    int x=0;bool fopt=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
    return fopt?x:-x;
}

int gcd(int x,int y){
    if(y==0)return x;
    return gcd(y,x%y);
}

int main(){
#ifndef LOCAL
    freopen("factory.in","r",stdin);
    freopen("factory.out","w",stdout);
#endif
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    int i=1;
    while(i<=n){
        if(i==n){
            ans++;break;
        }
        int d=abs(a[i+1]-a[i]);
        if(d==0||d==1){
            ans++;i++;continue;
        }
        s.insert(a[i]);s.insert(a[i+1]);
        int j;
        for(j=i+2;j<=n;j++){
            if(s.find(a[j])!=s.end())break;
            d=gcd(d,abs(a[j]-*s.begin()));
            if(d==1)break;
            s.insert(a[j]);
        }
        i=j;ans++;s.clear();
    }
    printf("%d
",ans);
    return 0;
}

T2:征途堆积出友情的永恒

(O(n^2)) 的暴力很好写,但是人傻了,忘记可以从出发点直接开到现在,而且特判的分数也没写全。

    memset(f,0x3f,sizeof(f));
    f[0]=0;//删掉60->15,人傻了
    f[1]=max(b[0],a[1]);
    for(register int i=2;i<=n;i++)
        for(register int j=i;j>=0&&i-j<=K;j--)
            f[i]=min(f[i],f[j]+max(b[j],sum[i]-sum[j]));
    ans=f[n];

简单的转移方程写出来之后,根据题目名字的提示我们想到了用堆优化转移。

对于 (j)(f[j]+b[j])(f[j]-sum[j]) 都是确定的。

那么就关于这两个东西维护两个小根堆。同时要注意因为在 (b[j]>sum[i]-sum[j]) 时第一个堆里的元素才会合法,所以首先把所有堆 1 里不合法的 pop,但要把它推入堆 2。

上面那个操作之前,之后,都要 pop 在距离上 (>k) 的点。如果之后不 pop ,会错一个点。

while 循环很带感

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,K;
long long ans;
int a[maxn],b[maxn];
long long sum[maxn],f[maxn];

struct Node{
    int x;
    long long w;
    friend inline bool operator <(register const Node& A,register const Node& B){
        return A.w>B.w;
    }
};

inline int read(){
    int x=0;bool fopt=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
    return fopt?x:-x;
}

priority_queue<Node> q1,q2;

int main(){
#ifndef LOCAL
    freopen("empire.in","r",stdin);
    freopen("empire.out","w",stdout);
#endif
    n=read();K=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=1;i<=n;i++)
        b[i-1]=read();
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++){
        q1.push((Node){i-1,f[i-1]+b[i-1]});
        while(!q1.empty()){
            Node x=q1.top();
            if(x.x>=i-K)break;
            q1.pop();
        }
        while(!q2.empty()){
            Node x=q2.top();
            if(x.x>=i-K)break;
            q2.pop();
        }
        while(!q1.empty()){
            Node x=q1.top();
            if(x.w>f[x.x]+sum[i]-sum[x.x])break;
            q1.pop();q2.push((Node){x.x,f[x.x]-sum[x.x]});
        }
        while(!q1.empty()){
            Node x=q1.top();
            if(x.x>=i-K)break;
            q1.pop();
        }
        while(!q2.empty()){
            Node x=q2.top();
            if(x.x>=i-K)break;
            q2.pop();
        }
        long long temp=0x3f3f3f3f3f3f3f3f;
        if(!q1.empty())temp=min(temp,q1.top().w);
        if(!q2.empty())temp=min(temp,q2.top().w+sum[i]);
        f[i]=temp;
    }
    printf("%lld
",f[n]);
    return 0;
}

T3:小奇的仓库

这个人更傻了,之前还写过题解的。

基础的换根 (DP) 的分怎么没拿到呢?快和小编来看一看吧。

void dfs2(int u,int fa){
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa)continue;
        f[v]=f[u]+(n-siz[v])*e[i].w-siz[v]*e[i].w;
        dfs2(v,u);//考场没写这句
    }
}

由于样例只有一层,所以我并没有看出来我写挂了:D。

题解

注意其算的是异或后的差值,所以最后统一修改是正确的。可以手模一下。

T4:放置机器人

二分图练习里就这道题没写:D。人都傻掉了。

考场上拆行拆列都写了,结果没想到最后打败我的是匈牙利的板子忘掉了。事实上,还是对匈牙利的本质没有弄清。

bool vis[maxn];
int match[maxn];
bool dfs(int u){
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v])continue;
        vis[v]=1;
        if(!match[v]||dfs(match[v])){//考场写的dfs(v)
            match[v]=u;return 1;
        }
    }
    return 0;
}

考虑墙对答案的影响,无非就是墙将本来属于一行的拆成了多行,将本来属于一列的拆成了多列。所以我们将行和列重新标号之后,就变成了裸的二分图放置问题。

#include <bits/stdc++.h>
using namespace std;
const int maxn=3000+10;
int n,m,ans;
char s[60][60];

struct Edge{
    int from,to,nxt;
}e[maxn<<1];

int head[maxn],cnt;
inline void add(int u,int v){
    e[++cnt].from=u;
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}

inline int Get(int x,int y){
    return m*(x-1)+y;
}

int belrow[maxn],belcol[maxn];

bool vis[maxn];
int match[maxn];
bool dfs(int u){
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(vis[v])continue;
        vis[v]=1;
        if(!match[v]||dfs(match[v])){
            match[v]=u;return 1;
        }
    }
    return 0;
}

int main(){
#ifndef LOCAL
    freopen("robots.in","r",stdin);
    freopen("robots.out","w",stdout);
#endif
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++){
        belrow[0]++;
        for(int j=1;j<=m;j++){
            if(s[i][j]=='o'){
                belrow[Get(i,j)]=belrow[0];
            }else if(s[i][j]=='#'&&j!=m&&s[i][j+1]!='#')belrow[0]++;
        }
    }
    for(int j=1;j<=m;j++){
        belcol[0]++;
        for(int i=1;i<=n;i++){
            if(s[i][j]=='o'){
                belcol[Get(i,j)]=belcol[0];
            }else if(s[i][j]=='#'&&i!=n&&s[i+1][j]!='#')belcol[0]++;
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(s[i][j]=='o')add(belrow[Get(i,j)],belcol[Get(i,j)]);
    for(int i=1;i<=belrow[0];i++){
        memset(vis,0,sizeof(vis));
        if(dfs(i))ans++;
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Midoria7/p/13770040.html