NENUACM 2020 复健训练 #4 题解

题目链接

Search for Pretty Integers

题意:给你两行数(均是个位数),第一行有n个数,第二行有m个数,让你构造一个最小的数满足至少包含一个第一行里的数且至少包含一个第二行里的数。

思路:显然,如果两行数里有相同的数,那么答案就是这些相同的数里的最小值;否则,以第一行里的数为十位数,以第二行里的数为个位数枚举,然后以第二行里的数为十位数,以第一行里的数为个位数枚举,取最小值即可。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
using namespace std;
int a[15],b[15];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)   scanf("%d",&b[i]);
    int ans=100;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i]==b[j])  ans=min(ans,a[i]);
            else    ans=min(ans,min(a[i]*10+b[j],b[j]*10+a[i]));
        }
    }
    printf("%d\n",ans);
    return 0;
}

Maximum of Maximums of Minimums

题意:给你n个数,让你把这n个数分成k个子段,找出每个子段里的最小值,然后求出这些最小值中的最大值,问这个最大值最大是多少。

思路:如果k等1,那么答案就是这n个数里的最小值;如果k等2,那么可以枚举所有的分法,然后求出最小值中的最大值;如果k大于等于3,那么答案就是这n个数里的最大值,因为此时你可以让最大值自己成为一段,然后把剩下的n-1个数分成k-1段。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=1e9+10;
const int maxn=1e5+10;
using namespace std;
int a[maxn],min1[maxn],min2[maxn];
//min1[i]表示a[1]到a[i]中的最小值
//min2[i]表示a[i+1]到a[n]中的最小值
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
    int minn=inf,maxx=-inf;
    for(int i=1;i<=n;i++)
        minn=min(minn,a[i]),maxx=max(maxx,a[i]);
    min1[0]=inf;
    for(int i=1;i<=n;i++)
        min1[i]=min(min1[i-1],a[i]);
    min2[n+1]=inf;
    for(int i=n;i>=1;i--)
        min2[i]=min(min2[i+1],a[i]);
    int ans=minn;
    if(k==1)
        ans=minn;
    else if(k==2)
    {
        for(int i=1;i<=n-1;i++)
            ans=max(ans,max(min1[i],min2[i+1]));
    }
    else
        ans=maxx;
    printf("%d\n",ans);
    return 0;
}

Maximum splitting

题意:让你回答q次询问,每次询问给你一个数n,问n最多能分成几个合数的和。

思路:我们希望分成的合数越小越好,而最小的合数是4,所以我们先按4分,不能整除4的话,我们进行分类讨论,把余数加到4上凑成合数就行了。若n%4==0,那么把n分成n/4个4的和就行了,故答案为n/4;若n%4==1,那么剩下的那个1要和两个4组成9,故答案为n/4-1;若n%4==2,那么剩下的那个2要和一个4组成6,故答案为n/4;若n%4==3,那么剩下的那个3要分成1和2,1和两个4组成9,2和一个4组成6,故答案为n/4-1。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=1e9+10;
const int maxn=1e5+10;
using namespace std;
int n,ans;
int main()
{
    int q;scanf("%d",&q);
    while(q--)
    {
        scanf("%d",&n);
        if(n%4==0)
            ans=n/4;
        else if(n%4==1)
        {
            if(n/4>=2)  ans=n/4-1;
            else    ans=-1;
        }
        else if(n%4==2)
        {
            if(n/4>=1)   ans=n/4;
            else    ans=-1;
        }
        else if(n%4==3)
        {
            if(n/4>=3)  ans=n/4-1;
            else    ans=-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

Something with XOR Queries

题意:有两个长度均为n的数组p和b,p是0到n-1的一个全排列,b表示数字在p中的位置。(即p[b[i]]=i,例如:n为4时,若p[0]=2,p[1]=3,p[2]=1,p[3]=0,则b[0]=3,b[1]=2,b[2]=0,b[3]=1。)给你n,然后你可以询问2*n次,每次询问时给出i,j,返回p[i]异或b[j]的结果,问p数组有多少种可能的情况,并输出任意一种。

思路:交互题。询问所有0,i和i,0,之后就能得知所有数的两两异或,(我们可以令c[i][j]=p[i]^b[j],然后我们可以得到c[i][j]=c[i][0] ^ c[0][j] ^c[0][0],因为:c[i][j]=p[i]^b[j]=p[i]^b[0]^p[0]^b[j]^p[0]^b[0]=c[i][0]^c[0][j]^c[0][0])然后就枚举第一个数,暴力计算即可。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=5e3+10;
using namespace std;
int n,p[maxn],b[maxn],c[maxn][maxn],anss[maxn];
bool ok()//判断数组p是否合法
{
    for(int i=0;i<n;i++)
        if(p[b[i]]!=i)
            return false;
    return true;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        printf("? 0 %d\n",i);
        fflush(stdout);
        scanf("%d",&c[0][i]);
    }
    for(int i=0;i<n;i++)
    {
        printf("? %d 0\n",i);
        fflush(stdout);
        scanf("%d",&c[i][0]);
    }
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            c[i][j]=c[i][0]^c[0][j]^c[0][0];
    int ans=0;
    for(int i=0;i<n;i++)
    {
        p[0]=i;
        b[i]=0;
        for(int j=0;j<n;j++)
        {
            p[j]=b[i]^c[j][i];
            b[j]=p[0]^c[0][j];
        }
        if(ok())
        {
            ans++;
            if(ans==1)
            {
                for(int j=0;j<n;j++)
                    anss[j]=p[j];
            }
        }
    }
    printf("! %d\n",ans);
    if(ans>0)
    {
        for(int i=0;i<n;i++)
        {
            if(i==0)    printf("%d",anss[i]);
            else    printf(" %d",anss[i]);
        }
    }
    printf("\n");
    return 0;
}

Points, Lines and Ready-made Titles

题意:给出n个二维点,坐标都是整数,每个点可以不画线,也可以画一条竖直的线,也可以画一条水平的线,但是最多只能画一条线。 求图案方案数。

思路:可以把这n个点分成若干个连通块,两个点连通当且仅当这两个点的横坐标相同、或者这两个点的纵坐标相同、或者这两个点同时和第三个点连通。先假设一个连通块内仅有一个点,这个连通块每加入一个点,会仅引入一条边或者不引入边,则:连通块内边的数目必然小于等于点的数目加1。那么我们可以分为两种情况(设边数为b,点数为d):1、b=d+1,每条边都有选和不选两种情况,故有2的b次方种情况,但是因为每个点最多画一条边,所以这个连通块最多产生d条边,所以应该减去选了b条边的那种情况,则此时这个连通块对答案产生的贡献是2的b次方减1;2、b<=d,每条边都有选和不选两种情况,则此时这个连通块对答案产生的贡献是2的b次方。所以用一下并查集,然后统计贡献即可。

参考博客:https://blog.csdn.net/calabash_boy/article/details/78249446(博客中作者有部分笔误)

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
const ll Mod=1e9+7;
using namespace std;
int n,cnt,x[maxn],y[maxn],fa[2*maxn];
map<int,int> xx,yy;
int b[2*maxn],d[2*maxn];//i集合中边的数目 i集合中点的数目
int findfa(int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
ll pow_mod(ll a,ll b,ll mod)
{
    ll ret=1;
    while(b)
    {
        if(b&1) ret=(ret*a)%mod,b--;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
int main()
{
    scanf("%d",&n);
    cnt=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        if(!xx.count(x[i]))
            xx.insert(make_pair(x[i],cnt++));
        if(!yy.count(y[i]))
            yy.insert(make_pair(y[i],cnt++));
    }
    cnt--;
    for(int i=1;i<=cnt;i++) fa[i]=i;
    for(int i=1;i<=n;i++)   fa[findfa(xx[x[i]])]=findfa(yy[y[i]]);
    for(int i=1;i<=n;i++)   d[findfa(xx[x[i]])]++;
    for(int i=1;i<=cnt;i++) b[findfa(i)]++;
    ll ans=1;
    for(int i=1;i<=cnt;i++)
    {
        if(fa[i]==i)
        {
            if(b[i]>d[i])   ans=ans*(pow_mod(2,b[i],Mod)-1)%Mod;
            else    ans=ans*pow_mod(2,b[i],Mod)%Mod;
        }
    }
    printf("%I64d\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值