【LGR-150-Div.2】洛谷 8 月月赛 I & RiOI Round 2

发布时间 2023-08-05 21:16:45作者: minecraft114514
# [【LGR-150-Div.2】洛谷 8 月月赛 I & RiOI Round 2](https://www.luogu.com.cn/contest/122184)
## T1 [P9496 「RiOI-2」hacker](https://www.luogu.com.cn/problem/P9496?contestId=122184)
$100pts$
### 题目描述
有两种操作,$「ACCEPT」$与$「BOTH」$,均花费 $1$ 代价。$「ACCEPT」$将 $n$ 二进制 按位或 一个正整数。$「BOTH」$将 $n$ 二进制 按位与 一个正整数。两种操作均可使用多次(或不用),请求出将 $n$ 变为 $m$ 最小的代价。

[帮助:什么是按位与和按位或](https://oi-wiki.org/math/bit)
- 水题,但是本蒟蒻一开始用 $scanf$ 输入 $long long$ 没有用 $"\%ld"$ 然后 $WA$ 了两个点。
  - 将 $n$ 和 $m$ 转化为二进制,用两个数组分别存起来,再枚举。
  - 若 $n$ 表示为 $0$,$b$ 表示为1,则进行$「ACCEPT」$操作。
  - 若 $m$ 表示为 $1$,$b$ 表示为0,则进行$「BOTH」$操作。
```cpp
#include<bits/stdc++.h>
using namespace std;
long long int n,sum=0;
long long int x,y;
long long int a[100],b[100];
int main()
{
    long long int i,j,m,t;
    scanf("%ld",&t);
    while(t--)
    {
        sum=0;
        scanf("%ld%ld",&n,&m);//scanf输入long long需要 "%ld"
        if(n==m)
        {
            printf("%d\n",0);
            continue;
        }
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        int cnt=0,res=0;
        int cn=0,cm=0;
        x=n,y=m;
        while(x)
        {
            a[++cnt]=x%2;
            x/=2;
        }
        while(y)
        {
            b[++res]=y%2;
            y/=2;
        }
        for(i=1;i<=max(cnt,res);i++)
        {
            if(a[i]==1&&b[i]==0)cn=1;
            if(a[i]==0&&b[i]==1)cm=1;
        }
        printf("%d\n",cn+cm);
    }
}
```

## T2 [P9497 「RiOI-2」weight](https://www.luogu.com.cn/problem/P9497?contestId=122184)
$100pts$
### 题目描述
给定一个 $n$ 行 $n$ 列 的矩阵 $a$。

有 $q$ 组询问,每次给定一个 $v$,请将矩阵每一行任意重排(可以不重排),最大化最大值不小于 $v$(也就是说,至少有一个不小于 $v$ 的数)的列数。请输出这个列数。

询问之间相互独立。换言之,每次询问前可以重新排列。

- 还是水题,将二维矩阵压成一位数组,输入后再从大到小 $sort$ 一遍,每次询问时,从 $1$ 到 $n$ 中寻找第一个小于 $v$ 的数,如果都大于等于 $v$ 则,输出 $n$ 。
  - 一开始没看懂题目,以为是把每行从大到小排列。
  - 改过来之后,发现输出都是 $0$ ,但本蒟蒻没想太多就交上去了,但是竟然 $AC$ 了,才想起来电脑是$32$位,$long long$ 显示为$0$。
```cpp
#include<bits/stdc++.h>
using namespace std;
long long int n,q,a[1010001];
long long int maxx[1001];
bool cmp(long long int x,long long int y)
{
    return x>y;
}
int main()
{
    int i,j;
    cin>>n>>q;
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%ld",&a[(i-1)*n+j]);
    stable_sort(a+1,a+1+n*n,cmp);
    for(i=1;i<=n;i++)maxx[i]=a[i];
    long long int w;
    for(i=1;i<=q;i++)
    {
        int flag=0;
        scanf("%ld",&w);
        for(j=1;j<=n;j++)
        {
            if(maxx[j]<w)
            {
                printf("%ld\n",j-1);
                flag=1;
                break;
            }
        }
        if(!flag)printf("%ld\n",n);
    }
}
```

## T3 [P9498 「RiOI-2」equals](https://www.luogu.com.cn/problem/P9498?contestId=122184)
$100pts$

### 题目描述

给定一棵 $n$ 个结点,以 $1$ 为根的树,定义一个结点的深度 $d_i$ 表示它到根结点的简单路径上的结点个数。

你需要给每个结点黑白染色,满足黑色结点的深度和等于白色结点的深度和。设 $c_i = \{0, 1\}$ 分别代表编号为 $i$ 的结点为黑色或白色,那么这即 $\displaystyle\sum_{c_i=0}d_i=\sum_{c_i=1}d_i$。

若无解,仅输出一行一个整数 $-1$。

- 本蒟蒻一开始打算用邻接矩阵存边,但是...



### 数据规模与约定



| $\rm Subtask$ | 分值 | $n\le $ | 特殊性质 |
| :-----------: | :--: | :-----: | :------: |
| $0$ | $5$ | $20$ | / |
| $1$ | $15$ | $500$ | / |
| $2$ | $20$ | $5\times 10^3$ | / |
| $3$ | $10$ | / | $n$ 为偶数 |
| $4$ | $5$ | / | 树为菊花图(不保证根为菊花中心) |
| $5$ | $5$ | / | 树为一条链(不保证根为链的端点) |
| $6$ | $40$ | / | / |

斜杠表示这一栏无特殊限制。

对于 $100\%$ 的数据,$1\le n\le 10^6$,$1\le u_i,v_i\le n$,输入数据构成一棵树。

  - 由于$1\le n\le 10^6$,所以改用链式前向星存边。
  - $dfs$ 一遍得出深度,由于黑点、白点的深度和要相等,所以想到前几天做的分钱,于是用$dp$判断是否有解。
  - 但是如果有解需要输出点的状态,而本蒟蒻不会记录路径,因此放弃了$dp$。
  - 放弃了$dp$,考虑爆搜,打搜索。因为爆搜超时,开始一分没骗到,然后剪枝得了$85$
  之后想到折半搜索,但是本蒟蒻只会折半,因此分了两个$for$循环,第一个从$1$循环到$(n+1)/2$
  第二个从$(n+1)/2$循环到$n$。(~~但应该不是正解~~)。然后$AC$......
 
 ### (~~出题人的数据怎么如此之氵,把爆搜都放过去了~~)...
[测评记录](https://www.luogu.com.cn/record/119207366)

```cpp
#include<bits/stdc++.h>
using namespace std;
long long int dep[6000001],vis[6000001];
long long int n,sum=0;
struct ee
{
    int next,to;
}e[6000001];
int head[6000001],cnt=0;
void add(int u,int v)
{
    e[++cnt]={head[u],v};
    head[u]=cnt;
}
void dfs(int x)
{
    int i;
    vis[x]=1;
    for(i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(!vis[v])
            dep[v]=dep[x]+1,dfs(v);
    }
}
bool pd[6000001];
void search(int x,long long int s)
{
    int i,j;
    if(s==(sum/2))
    {
        for(j=1;j<=n;j++)
        {
            if(pd[j])printf("%d ",1);
            else printf("%d ",0);
        }
        exit(0);
    }
    for(i=x;i<=(n+1)/2;++i)
    {
        if(!pd[i]&&s+dep[i]<=(sum/2))
        {
            pd[i]=1;
            if(s+dep[i]==(sum/2))
            {
                for(j=1;j<=n;j++)
                {
                    if(pd[j])printf("%d ",1);
                    else printf("%d ",0);
                }
                exit(0);
            }
            if(s+dep[i]<(sum/2))
                search(i+1,s+dep[i]);
            pd[i]=0;
        }
    }
    for(i=(n+1)/2;i<=n;++i)
    {
        if(!pd[i]&&s+dep[i]<=(sum/2))
        {
            pd[i]=1;
            if(s+dep[i]==(sum/2))
            {
                for(j=1;j<=n;j++)
                {
                    if(pd[j])printf("%d ",1);
                    else printf("%d ",0);
                }
                exit(0);
            }
            if(s+dep[i]<(sum/2))
                search(i+1,s+dep[i]);
            pd[i]=0;
        }
    }
}
int main()
{
    int i,j,m,x,y;
    scanf("%d",&n);
    for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dep[1]=1;
    dfs(1);
    for(i=1;i<=n;++i)sum+=dep[i];
    if(sum%2==1)
    {
        printf("%d",-1);
        return 0;
    }
    search(1,0);
    printf("%d",-1);
}
```



## T4 [P9499 「RiOI-2」change](https://www.luogu.com.cn/problem/P9499?contestId=122184)
$20pts$
- 本蒟蒻不会打,骗了20分。。。