AtCoder Beginner Contest 396

AtCoder Beginner Contest 396

A - Triple Four

给定一个数列,问是否存在连续三个数是一样的。

简单的判断,枚举第一个数的位置,然后检查它和后面两个数是否一致。

#include<iostream>
using namespace std;
int n;
int a[101];
bool fl=false;
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=n-2;++i)
        if(a[i]==a[i+1]&&a[i]==a[i+2])
            fl=true;
    puts(fl?"Yes":"No");
    return 0;
}

B - Card Pile

有一个栈,里面有100个卡片。每个卡片上都是0。

执行Q次操作,每次操作可能是:

  • 将一个写着数字x的卡片置于栈顶
  • 将栈顶的卡片移除,并输出其卡片上的数字
#include<iostream>
using namespace std;
int S[202],top;
int Q;
int main()
{
    for(int i=1;i<=100;++i)S[++top]=0;
    cin>>Q;
    while(Q--)
    {
        int a,x;
        cin>>a;
        if(a==1)
        {
            cin>>x;
            S[++top]=x;
        }
        else
            cout<<S[top--]<<endl;
    }
    return 0;
}

C - Buy Balls

有N个黑球,M个白球。

每个球上都有一个数字,第\(i\)个黑枪上的数字是\(B_i\),第\(j\)个白球上的数字是\(W_j\)

现在要选择若干个球,使得黑色球的数量不少于白色球。

问所有选取方案中,球上的数字和最大是多少。

枚举白色球的数量,这样白色球一定会选数字最大的那些。

在确定了白色球的选择之后,黑色球可以这样贪心的选择:

  • 如果黑色球非负数的数量大于白色球选择的数量,则全选这些黑球
  • 否则选择最大的、数量与白色球相等的黑色球
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 200200
int n,m;
int W[MAX],B[MAX];
long long ans=-1e18,sum=0;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&B[i]);
    for(int i=1;i<=m;++i)scanf("%d",&W[i]);
    sort(&B[1],&B[n+1]);
    sort(&W[1],&W[m+1]);
    int cntB=0,cntW=0;
    for(int i=n;i;--i)
        if(B[i]>=0)++cntB,sum+=B[i];
        else break;
    ans=sum;
    for(int j=m;j;--j)
    {
        ++cntW;
        if(cntW<=cntB)sum+=W[j];
        else
        {
            if(cntW>n)break;
            sum+=W[j]+B[n-cntW+1];
        }
        ans=max(ans,sum);
    }
    cout<<ans<<endl;
    return 0;
}

D - Minimum XOR Path

给定一张连通图,有\(n\)个点\(m\)条边。每条边有一个边权。

现在要选择一条路径,一条路径是指由边相连的一系列边,且不经过任何重复的点。

问所有从1出发到达N的路径中,经过的所有边的边权的异或和的最小值。

直接dfs所有可能的路径,依次检查即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
ll ans=-1;
int n,m;
struct Edge{int v,nt;ll w;}e[1000];
int h[15],cnt=0;
void add(int u,int v,ll w)
{
    e[++cnt]=(Edge){v,h[u],w};
    h[u]=cnt;
}
bool vis[15];
void dfs(int u,ll val)
{
    if(u==n)
    {
        if(ans==-1)ans=val;
        else ans=min(ans,val);
        return;
    }
    for(int i=h[u];i;i=e[i].nt)
    {
        int v=e[i].v;ll w=e[i].w;
        if(!vis[v])
        {
            vis[v]=true;
            dfs(v,val^w);
            vis[v]=false;
        }
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;++i)
    {
        int u,v;ll w;
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,w);
    }
    vis[1]=true;
    dfs(1,0);
    cout<<ans<<endl;
    return 0;
}

E - Min of Restricted Sum

给定N、M,还有三个长度为M的序列X、Y、Z。保证X和Y中的每一个元素都在1到N之间。

我们称一个长度为N的非负序列A是好的,当且仅当对于所有的\(1\le i\le M\),满足\(A_{x_i}\oplus A_{y_i}=Z_i\)

判断是否存在好的序列,如果存在,输出所有元素和最小的那一个。

首先判断序列是否存在。

构建一个\(N\)个点的图,对于\(X_i\)\(Y_i\),在其间连一条边。这意味着,一旦我们知道了其中任何一个数是\(x\),那么我们就可以推出另一个数是\(Z_i\oplus x\)

只要这些边不构成环,那么它们一定不会发生冲突。

但如果出现了环,我们需要沿着环检查所有边对应的\(Z_i\)的异或和是否为\(0\),如果不是,那么意味着会出现冲突,不可能符合条件,也即无法构建合法的序列。

那么如果没有冲突,只需要分连通块考虑。而一个连通块中,我们可以知道任何两个数之间的异或值。且只需要知道一个数,剩下所有数都唯一确定。

把上面这个过程重新理解一下,实际上我们只需要指定连通块中的某个点为根节点,其他点连接构成树,维护其到根节点的路径上的异或和。如果出现非树边,检查构成的环是否异或和为\(0\)即可。

对于合法的连通块,考虑根节点的取值。而异或和可以逐位考虑,因此对于每个二进制位,枚举根节点选择1或者0,这样就可以知道其他的所有节点是0还是1,选择其中1较少的那个即可。这样贪心构造就可以得到所有数的值。

一些推论:不难证明如果\(a\oplus b=c\),那么\(a+b\ge c\)。且我们发现当\(a\)或者\(b\)\(0\)的时候,有\(a+b=c\)。因此,通过构造可以保证一定存在一个解,满足每个连通块里面一定有一个数会是\(0\)

F - Rotated Inversions

给定整数N,M和一个长度为N的非负整数序列A。

对于k=0,1,...,M-1,解决如下问题:

定义一个长度为N的整数序列B,满足\(B_i\)\(A_i+k\)除以M的余数。求出B中的逆序对的数量。

显然,要做的就是令$B_i=A_i\ mod\ M \(,然后每次令所有\)B_i$加一,模M。

注意到,如果没有模M这个操作,那么B中的数字的相对大小关系是不会发生改变的,因此逆序对的数量也不可能发生改变。

因此,只有模M会影响到相对大小关系,且当且仅当在某次加一取模之后变成0的数才会带来影响。

而0比任何其他数都小,其他数都因为加一且取模之后不会变小必定会比0大。因此0和其他所有在他之前的数除0外的所有数都必定构成逆序对。

而原本的M-1比其他数都大,和在它之后的、除M-1之外的所有数都必定构成逆序对。

于是问题就变成了,从k=0开始,计算逆序对,然后确定每次哪些数加一取模之后会变成0,计算一下逆序对的数量的变化值即可。

至于怎么求最开始的逆序对数量,可以用值域上的数据结构或者分治在一个log内完成计算。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 200200
int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,m,a[MAX];
int t[MAX];
int lowbit(int x){return x&(-x);}
void add(int x,int a){while(x<=m)t[x]+=a,x+=lowbit(x);}
int getsum(int x){int r=0;while(x)r+=t[x],x-=lowbit(x);return r;}
long long ans=0;
vector<int> V[MAX];
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        a[i]=read();
        V[m-a[i]].push_back(i);
        ans+=getsum(m-a[i]-1);
        add(m-a[i],1);
    }
    printf("%lld\n",ans);
    for(int k=1;k<m;++k)
    {
        for(int j=0,l=V[k].size();j<l;++j)
        {
            int p=V[k][j];
            ans-=n-p-(l-j-1);
            ans+=p-1-j;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

G - Flip Row or Col

给定一个H行W列的01矩阵,每次可以翻转一行或者一列上的所有01元素,可以执行若干次操作。

问矩阵通过若干次操作之后,1的数量的最小值。

注意到H很大,W很小。

所以我们把每一行看成一个二进制数\(B_i\),翻转若干列等于所有数都异或上某个数\(X\)

\(f[X][j][c]\)表示在所有满足\(B_i\in\{X\oplus a|0\le a\le 2^j\}\)\(B_i\)中,\(X\oplus B_i\)二进制位上有\(c\)\(1\)的数的数量。

那么\(f[X][j+1][c]+=f[X][j][c],f[X][j+1][c+1]+=f[X\oplus 2^j][j][c]\)

这样子我们就可以知道对于每个\(X\),其和每个\(B_i\)异或之后,有多少位不同。

所以对于固定的\(X\),1的最小值为\(\sum_{c=0}^W f[X][W][c]\times \min{\{c,W-c\}}\)

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 200200
int a[MAX];
int f[20][20][1<<18];
int H,W,ans=1e9;
char s[20];
int main()
{
    cin>>H>>W;
    for(int i=1;i<=H;++i)
    {
        scanf("%s",s);
        for(int j=0;j<W;++j)a[i]=a[i]<<1|(s[j]-48);
        f[0][0][a[i]]+=1;
    }
    for(int j=1;j<=W;++j)
        for(int c=0;c<=W;++c)
            for(int k=0;k<(1<<W);++k)
            {
                f[j][c][k]+=f[j-1][c][k];
                f[j][c+1][k]+=f[j-1][c][k^(1<<(j-1))];
            }
    for(int k=0;k<(1<<W);++k)
    {
        int sum=0;
        for(int c=0;c<=W;++c)
            sum+=f[W][c][k]*min(c,W-c);
        ans=min(ans,sum);
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2025-03-15 16:02  小蒟蒻yyb  阅读(125)  评论(3)    收藏  举报