2020-2021 Winter Petrozavodsk Camp, Belarusian SU Contest (XXI Open Cup, Grand Prix of Belarus)

题目链接

C. Brave Seekers of Unicorns
给出一个好数组的定义:

1. 1. 1. 长度不为空

2. 2. 2. a [ i ] ⨁ a [ i − 1 ] ⨁ a [ i − 2 ] ≠ 0 a[i] \bigoplus a[i-1] \bigoplus a[i-2] \neq 0 a[i]a[i1]a[i2]=0

3. 3. 3. 严格递增

求出有多少个满足条件的数组,且元素值在 ≤ n \leq n n

题解:

d p [ i ] dp[i] dp[i] 表示以 i i i 结尾的数组的方案数,那么可以列出转移方程

d p [ i ] = ∑ j = 1 i − 1 ( d p [ j ] − d p [ i ⨁ j ] ) dp[i]= \sum\limits_{j=1}^{i-1} (dp[j]-dp[i \bigoplus j]) dp[i]=j=1i1(dp[j]dp[ij]) ( i ⨁ j < j ) ( i \bigoplus j < j ) (ij<j)

那么维护前缀 d p dp dp的和,再求出 i ⨁ j i \bigoplus j ij 有哪些数,减一下即可,怎么求有哪些数?

通过打表可以发现,对于二进制下的 i i i ,如果第 x x x 位为 1,那么 2 x 2^x 2x 2 x + 1 − 1 2^{x+1}-1 2x+11 之间的数都是(除了最高位的1除外)

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=1e6+5;
const int mod=998244353;
const int inf=0x3f3f3f3f;
ll sum[MAXN],dp[MAXN];
int main()
{
    
    
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    
    
        dp[i]=sum[i-1]+1;
        int temp=i;
        int x=1;
        while(temp)
        {
    
    
            if((temp&1)&&temp!=1)
            {
    
    
                dp[i]=(mod+dp[i]-sum[min(x*2-1,i)]+sum[min(x-1,i)])%mod;
                //cout<<min(x,i-1)<<" "<<min(x*2-1,i-1)<<endl;
            }
            temp>>=1;
            x=x*2;
        }
        sum[i]=(sum[i-1]+dp[i])%mod;
    }
    printf("%lld\n",sum[n]);
}

J - Burnished Security Updates
给出一个图,判断是否是二分图,如果是,则输出最少的点数,使得每条边都与只其中一个点有关联。

题解:
只需用二分图染色法判断二分图即可,在染色过程中,记录两种颜色的数量,最后把数量少的算在答案的贡献里。为什么?
因为一条边中只有其中一个点被选,当一个点被选了,那么与它相连的点就不能被选,这个过程就是染色的过程,所以把数量少的作为答案。
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=3e5+5;
const int mod=99824353;
const int inf=0x3f3f3f3f;
const int p=31;
struct node
{
    
    
    int to;
    int next;
}e[MAXN<<1];
int head[MAXN];
int cnt;
int vis[MAXN];
int colar[MAXN];
int flag,sum1,sum2;
void add(int u,int v)
{
    
    
    e[cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u,int co)
{
    
    
    vis[u]=1;
    if(co==1) sum1++;
    else sum2++;
    if(flag) return;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
    
    
        int v=e[i].to;
        if(vis[v])
        {
    
    
            if(colar[v]==co)
            {
    
    
                flag=1;
                return;
            }
        }
        else
        {
    
    
            colar[v]=3-co;
            dfs(v,colar[v]);
        }
    }
}
int main()
{
    
    
    memset(head,-1,sizeof head);
    int n,m;
    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);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
    
    
        flag=0;
        sum1=sum2=0;
        if(!vis[i])
        {
    
    
            colar[i]=1;
            dfs(i,1);
        }
        if(flag)
        {
    
    
            break;
        }
        ans+=min(sum1,sum2);
    }
    if(flag)
    {
    
    
        printf("-1\n");
        return 0;
    }
    printf("%d\n",ans);
}

D. Bank Security Unification
给出一个长度为 n n n 的数组,求一个子序列,使得 ∑ i = 1 k − 1 ( f [ i ] & f [ i + 1 ] ) \sum\limits_{i=1}^{k-1} (f[i] \& f[i+1]) i=1k1(f[i]&f[i+1]) 最大。

题解:

直接把队友的题解拿来

d p [ i ] dp[i] dp[i]表前 i i i个子序列的最大答案

首先很容易想到一个 O ( n 2 ) O(n^2) O(n2) d p dp dp

但是显然这样会超时

这样你肯定会想到用二进制优化 d p dp dp,然后就没有然后了。。。

此题真的有点神奇

下面我将进行不太严格的证明

定义 h ( x ) h(x) h(x)为不大于x的最大2的幂次

假设我选了最优子序列

f j 1 , f j 2 . . . f j k f_{j1},f_{j2}...f_{jk} fj1,fj2...fjk

j 1 ≤ t e m p ≤ j 2 j1≤temp≤j2 j1tempj2

那么必然在不存在 h ( f t e m p & f j 1 ) = h ( f j 2 & f j 1 ) h(f_{temp} \& f_{j1})=h(f_{j2} \& f_{j1}) h(ftemp&fj1)=h(fj2&fj1)

原因是为什么

为了方便设 x = f j 1 , y = f t e m p , z = f j 2 x=f_{j1},y=f_{temp},z=f_{j2} x=fj1,y=ftemp,z=fj2

那么假设我 y y y存在

那么序列变成 x , y , z , . . . x,y,z,... x,y,z,...

若不在序列则变成 x , z . . . . x,z.... x,z....

显然答案的变化为

x & y + y & z − x & z x \& y+y \& z−x \& z x&y+y&zx&z

显然这个值是大于0的,那么放入 y y y才是最优选择

而这个序列却已经是最优序列,代表中间没有 y y y

也就是说我们其实只要考虑二进制中的每一位的最后出现的位置即可

因为这个二进制的这一位肯定包含和这个元素和他前面的元素的 & \& &的最高位

代码:

#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int>pii;
const int MAXN=1e6+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
ll dp[MAXN];
ll a[MAXN];
ll pre[MAXN];
ll ans=0;
int main()
{
    
    
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++)
    {
    
    
        dp[i]=dp[i-1];
        for(int j=0;j<=50;j++)
        {
    
    
            if(a[i]&(1ll<<j))
            {
    
    
                dp[i]=max(dp[i],dp[pre[j]]+(a[pre[j]]&a[i]));
                pre[j]=i;
            }
        }
        //ans=max(ans,dp[i]);
    }
    printf("%lld\n",dp[n]);
}

猜你喜欢

转载自blog.csdn.net/weixin_45755679/article/details/114597690