AGC004

话说我稍微看了看之后的AGC,B题就开始蓝了。所以我觉得会写的越来越慢。现在已经开始一天写一套有点吃力了。

[AGC004A] Divide a Cuboid

普及-。稍微想了一下要不要继续切普及-。但是为了保证vp的完整性和鉴于CSP-S前一天衅心塞T1普及-没做出来还是做一下。

(没准我真就菜到普及-都不会了呢是吧

int a,b,c;
int main(){
    scanf("%d%d%d",&a,&b,&c);
    if((a&1)&&(b&1)&&(c&1))printf("%lld\n",min(1ll*a*b,min(1ll*b*c,1ll*a*c)));
    else puts("0");
    return 0;
}

[[AGC004B] Colorful Slimes]

本来想把紫以前的都手速掉结果被这个卡了一下。难点主要在想到这是个 dp。

\(dp_{i,j}\) 为用 \(j\) 次以内魔法得到 \(i\) 的最小代价。

#define int long long
int n,x,ans=__LONG_LONG_MAX__,a[2010],dp[2010][2010];
signed main(){
    scanf("%lld%lld",&n,&x);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        dp[i][0]=a[i];
        for(int j=1;j<n;j++){
            int ret=i-j;
            if(ret<=0)ret+=n;
            dp[i][j]=min(dp[i][j-1],a[ret]);
        }
    }
    for(int i=0;i<n;i++){
        int ret=0;
        for(int j=1;j<=n;j++)ret+=dp[j][i];
        ans=min(ans,ret+i*x);
    }
    printf("%lld\n",ans);
    return 0;
}

[AGC004C] AND Grid

写到这的时候已经开始难受了,因为看到 E 也是个网格图。

小清新构造,首先可以直接随便分把整张图分成两部分,构造的两个网格一个占一部分,然后把所有原图有的点让它在两个网格里都有就行了。然后他要保证连通,那就让一开始分成两部分之后随便加点都连通就行了。一种构造是把最左边一列和所有奇数行给第一个网格,最右边一列和偶数行给第二个网格。

int n,m;
char s[510][510],s1[510][510],s2[510][510];
signed main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)s1[i][j]=s2[i][j]='.';
    for(int i=1;i<=n;i++)s1[i][1]=s2[i][m]='#';
    for(int i=1;i<=n;i+=2){
        for(int j=1;j<m;j++)s1[i][j]='#';
    }
    for(int i=2;i<=n;i+=2)for(int j=2;j<=m;j++)s2[i][j]='#';
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(s[i][j]=='#')s1[i][j]=s2[i][j]='#';
        }
    }
    for(int i=1;i<=n;i++)printf("%s\n",s1[i]+1);
    printf("\n");
    for(int i=1;i<=n;i++)printf("%s\n",s2[i]+1);
    printf("\n");
    return 0;
}

[AGC004D] Teleporter

不知道为啥这种题我第一次交永远都会挂。是诅咒吗。

首先 \(1\) 号点显然必须连自己。然后剩下的点从叶子开始每 \(k\) 个连一次到 \(1\) 就行了。

然后注意 dfs 的时候 \(1\) 号点也可能会被算到每 \(k\) 个连一次的点内。判掉。

int n,ans;
struct node{
    int v,next;
}edge[100010];
int t,k,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int dfs(int x,int f,int d){
    int ret=d;
    for(int i=head[x];i;i=edge[i].next){
        ret=max(ret,dfs(edge[i].v,x,d+1));
    }
    if(f!=1&&ret-d==k-1){
        ans++;return 0;
    }
    return ret;
}
signed main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        int f;scanf("%d",&f);
        if(i==1&&f!=1)ans++;
        else if(i!=1)add(f,i);
    }
    dfs(1,1,0);
    printf("%d\n",ans);
    return 0;
}

[AGC004E] Salvage Robots

纯 ex 人的题。

一开始猜了个从 E 开始左上左下右上右下四个矩形求最大值,但是显然是假的。

这个机器人移动很麻烦,不如使用相对论,变成让出口移动。这样我们就相当于出口拖着一个能救出的机器人的矩形范围在动。

开四维 \(dp_{l,r,u,d}\) 为上下左右的步数大力转移就行了。卡空间要开short。转移略,但是及其 ex。

int n,m,x,y;
short ans,dp[110][110][110][110],sum1[110][110],sum2[110][110];
char s[110][110];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            sum1[i][j]=sum1[i-1][j];
            sum2[i][j]=sum2[i][j-1];
            if(s[i][j]=='E')x=i,y=j;
            else if(s[i][j]=='o')sum1[i][j]++,sum2[i][j]++;
        }
    }
    for(int l=0;l<y;l++){
        for(int r=0;r<=m-y;r++){
            for(int u=0;u<x;u++){
                for(int d=0;d<=n-x;d++){
                    ans=max(ans,dp[l][r][u][d]);
                    if(l+r<y-1)dp[l+1][r][u][d]=max(int(dp[l+1][r][u][d]),dp[l][r][u][d]+sum1[min(x+d,n-u)][y-l-1]-sum1[max(x-u-1,d)][y-l-1]);
                    if(l+r<m-y)dp[l][r+1][u][d]=max(int(dp[l][r+1][u][d]),dp[l][r][u][d]+sum1[min(x+d,n-u)][y+r+1]-sum1[max(x-u-1,d)][y+r+1]);
                    if(u+d<x-1)dp[l][r][u+1][d]=max(int(dp[l][r][u+1][d]),dp[l][r][u][d]+sum2[x-u-1][min(y+r,m-l)]-sum2[x-u-1][max(y-l-1,r)]);
                    if(u+d<n-x)dp[l][r][u][d+1]=max(int(dp[l][r][u][d+1]),dp[l][r][u][d]+sum2[x+d+1][min(y+r,m-l)]-sum2[x+d+1][max(y-l-1,r)]);
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

[AGC004F] Namori

神仙基环树题。做这题之前看了看今天的图论,贺了个次小生成树的板子(并查集甚至写挂了wssb)然后就跑路了。

首先特判 \(n\) 是奇数无解。然后这题第一步就卡死我了,遂∑。

题目给的这个操作很诡异。观察发现(不要问我怎么观察发现)这个可以把原图的节点黑白间隔染色,然后每次操作就相当于交换颜色不同的一对点的颜色,然后我们的目标就是把所有点的颜色对调。这个适用于树和偶环的情况,因为它们都是二分图。

先看树。假如说一个节点 \(x\) 的子树有 \(a\) 个黑点, \(b\) 个白点,那么连接 \(x\) 和父亲的这条边就至少要交换黑点和白点数量差值个点(把多出的点往上送,换成少的点)。这样构造可以使得所有子树内部都是黑白点数相等,而且操作次数最小。直接搜一遍就行了。

然后是偶环。先把环断一条边掉按照树处理,然后考虑环的贡献。环的贡献显然只对环上的点有用。然后如果对断掉的边操作 \(x\) 次,那么环上一定有一部分边多向上操作 \(x\) 次,同时有一部分边多向下操作 \(x\) 次。容易发现 \(x\) 一定是所有环上的答案的中位数,重新把环上的答案计算一遍即可。

最后是奇环。奇环上一定有两个点颜色相同,那么对这条两端点颜色相同的边的一次操作就相当于加上/删掉两个黑点。假如黑点和白点数量差值为 \(x\) ,那么我们只需要对这条边操作 \(\dfrac x2\) 次把黑白点数量调平,然后把这条边干掉就行了。

struct node{
    int v,next;
}edge[200010];
int n,m,t,S,T,ans,head[100010],col[100010],siz[100010];
int a[100010];
bool jud;
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs1(int x,int f,int c){
    col[x]=c;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            if(col[edge[i].v]){
                if(col[x]==col[edge[i].v])jud=true;
                S=x;T=edge[i].v;
            }
            else dfs1(edge[i].v,x,-c);
        }
    }
}
void dfs2(int x,int f){
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v==f||(edge[i].v==S&&x==T)||(edge[i].v==T&&x==S))continue;
        dfs2(edge[i].v,x);
        col[x]+=col[edge[i].v];siz[x]+=siz[edge[i].v];
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    if(n&1){
        puts("-1");return 0;
    }
    dfs1(1,0,1);
    int cnt=0;
    for(int i=1;i<=n;i++)cnt+=col[i];
    if(m==n-1){
        if(cnt){
            puts("-1");return 0;
        }
    }
    else{
        if(jud){
            ans+=abs(cnt>>1);col[S]-=cnt>>1;col[T]-=cnt>>1;
        }
        else{
            if(cnt){
                puts("-1");return 0;
            }
            else siz[S]=1,siz[T]=-1;
        }
    }
    dfs2(1,0);
    for(int i=1;i<=n;i++){
        if(siz[i])a[++a[0]]=col[i]*siz[i];
        else ans+=abs(col[i]);
    }
    a[++a[0]]=0;
    sort(a+1,a+a[0]+1);
    for(int i=1;i<=a[0];i++)ans+=abs(a[i]-a[(a[0]+1)>>1]);
    printf("%d\n",ans);
    return 0;
}

完了。

posted @ 2022-11-14 11:23  gtm1514  阅读(46)  评论(2编辑  收藏  举报