首页 > 其他分享 >暑假集训Day19 比赛题解

暑假集训Day19 比赛题解

时间:2023-09-08 10:47:44浏览次数:35  
标签:le ll int 题解 sum 集训 ans Day19 deg

2023-08-05 16:22:13

总结

这次打下来,由于 T2 贪心不够完全,T3 模拟 \(5\) 个时不是最优,T4 想到暴力做法但是来不及打,加之全都是捆绑测试点,导致我 T2,T3 虽然加起来有不少点对了,但是还是判全错,最后也只剩下 T1 的 100。

感觉这次前三题也不难,都是可做的,T4 的 30pts 暴力也很白给,但是我想加调了两个小时 T2 的假树形 dp,然后就来不及打了。

赛后发现 T2 的贪心和我想的基本一致,但是枚举的东西略有不同,也就是我的不够贪心。T3 的构造不难想,有一个挺显然的性质但是我并没有去在意。T1 有可以不用数位 dp 的更好方法,但我也没去找了。

总而言之,下次可以不需要这么心急,可以先打个暴力当对拍都不错,不过也挺好了,感觉自己的算法思维也越来越强了呢,还是有不错的提升的。

T4 的正解因为我还没有看懂,所以就不放了,等我自己想明白了再来搞。

那还是先进入正题吧~

A

题目描述

有 \(n+m\) 堆石子,\(n\) 堆中第 \(i\) 堆有 \(i\) 个石子,\(m\) 堆中第 \(i\) 堆中有 \(a_i\) 堆石子,问你进行 \(nim\) 游戏时先手第一步有多少种取法能保证自己获胜。(第一步之后均采用最优策略)。

\(1\le n\le 10^{18},1\le m\le 10^5,1\le a_i\le 10^{18}\)。

思路

令 \(sum=\bigoplus_{i=1}^{n}i\ \oplus\bigoplus_{i=1}^{m}a_i\)(即所有元素的亦或和)。

因为我上课的时候证明 \(nim\) 游戏的正确性的时候没有认真听,所以一开始以为我从大于等于 \(sum\) 的数中取 \(sum\) 个即可。但是显然用脑子想想都发现不对,我甚至还因为这个去问出题人要样例的解释,后来自己模拟一遍才发现不对劲,完全不是一个东西。

思考正解,设我们选了取个数为 \(x\) 的一堆,假设取后是 \(x'\) 那么 \(sum'=sum\oplus x \oplus x'\),因为要保证必胜,学过 \(nim\) 的都知道最后要保证 \(sum'=0\),所以我们也可以得到: \(sum\oplus x=x'\)。因为是取后,所以 \(x'<x\),即 \(sum\oplus x<x\)。

所以最终只需要求 \(sum\oplus x<x\) 的数的个数即可。

对于 \(m\le 10^5\),我们直接枚举 \(a_i\) 即可,但是 \(n\leq 10^{18}\),总不能一个一个枚举吧?

此时的瓶颈有两个了,第一个是 \(n\) 堆的异或和不好求,第二个是答案也不好求。

首先解决异或的问题,我们通过打表可以得到一个结论,当 \(n \mod 4=3\) 时,\(n\) 的前缀异或和为 \(0\)。

证明也很简单:

我们设 \(n\) 是 \(4\) 的倍数,那么二进制下:

\[n=?????00 \]

\[n+1=?????01 \]

\[n+2=?????10 \]

\[n+3=?????11 \]

显然 \(n\oplus(n+1)\oplus(n+2)\oplus(n+3)=0\),得证。

设 \(sum\) 最高位是第 \(k\) 位。

然后再通过分类讨论可以得到只有当 \(x\) 的第 \(k\) 位为 \(1\) 时满足条件。

所以机智的我懒得思考有的没的,直接现场 \(3\) 分钟打了一个数位 dp。

\(Code\)

ll n,m,sum,k,a[100005],f[2][2][102],A[102];
ll dfs(ll len,ll lim,ll isk){
    if(~f[lim][isk][len])return f[lim][isk][len];
    if(!len)return f[lim][isk][len]=isk;
    ll maxx=lim?A[len]:1,res=0;
    for(ll i=0;i<=maxx;i++){
        if(len==k+1)res+=dfs(len-1,lim&(i==maxx),i==1);
        else res+=dfs(len-1,lim&(i==maxx),isk);
    }
    return f[lim][isk][len]=res;
}
ll solve(ll x){
    memset(f,-1,sizeof(f));
    ll cnt=0;
    while(x){
        A[++cnt]=x&1;
        x>>=1;
    }
    return dfs(cnt,1,0);
}
int main(){
    n=read(),m=read();
    for(ll i=1;i<=m;i++){
        a[i]=read();
        sum^=a[i];
    }
    if(n%4==0)sum^=n;
    else if(n%4==1)sum^=(n-1)^n;
    else if(n%4==2)sum^=(n-2)^(n-1)^n;
    if(!sum)puts("0");
    else{
        ll ans=0;
        k=__lg(sum);
        for(ll i=1;i<=m;i++){
            if(a[i]&(1ll<<k))ans++;
        }
        ans+=solve(n);
        cout<<ans;
    }
}

求 \(1\sim n\) 中的答案其实是可以推式子的,我们把二进制的 \(n\) 分成 \([1,k-1],[k+1,k_n]\)两个部分,当 \(n\) 的第 \(k\) 为 \(1\) 时,答案就是两部分的总方案数相乘,如果为 \(0\),那也只有 \([k+1,k_n]\) 为原部分的方案数不行。

核心代码

    ll ans=0;
    k=__lg(sum);
    for(ll i=1;i<=m;i++){
        if(a[i]&(1ll<<k))ans++;
    }
    ans+=(n>>k+1ll)*(1ll<<k);//先把不含自己的方案数算上
    ans+=max(n%(1ll<<k+1ll)-(1ll<<k)+1ll,0ll);//再加上如果 k 位有1的方案数
    cout<<ans;

总得来说还是简单的,但是因为好不容易找出正解了考场上打出来也是挺开心的(打的比较细心,基本上就微调过大样例),后来发现原来不需要这样麻烦,甚至有些神仙通过打表找规律找到正解,悲伤。(郑神!!!%%%%)

B

问题描述

给你一棵树,每个节点得染成红色或者蓝色,\(i\) 点染成红色的代价为 \(a_i\),染成蓝色为 \(b_i\),最后必须保证对于每个点,它的子树内的点(含己)上面的红点总数和蓝点总数的差的绝对值不超过 \(1\),求最小代价。

\(3\le n\le 10^6\)

思路

一眼树形 dp,但是不知道具体怎么做,开始是想把每个儿子节点看成物品然后跑背包的,奈何本人技术力不够打不来这种背包,然后就没想着去拿这 \(O(n^2)\) 的 \(40\) 分。

还是看限制条件吧,“对于任意一个点,子树内\(\dots\)”,那我们不难发现对于每个子树只有三种情况:\(R=B,R=B+1,R=B-1\)(\(R\) 表示子树内红色点个数,\(B\) 表示子树内红色点个数)。

并且,我们观察到如果一个子树的大小是偶数,那只可能出现蓝色等于红色的情况,这种情况因为蓝色等于红色,怎么加都不会不合法,所以保证代价最小即可,不用理会。

而当大小为奇数时,有两种情况。

所以我们设计状态 \(f[0(R=B)/1(R=B+1)/2(R=B-1)][u]\) 表示以 \(u\) 为根节点的子树,红蓝配比为 \((R=B)/(R=B+1)/(R=B-1)\) 的最小代价,仔细思考不难发现转移:

\[f[0][u]=min(ans_{R=B+1}+b[u],ans_{R=B-1}+a[u])\\ f[1][u]=min(ans_{R=B+2}+b[u],ans_{R=B}+a[u])\\ f[2][u]=min(ans_{R=B-2}+a[u],ans_{R=B}+b[u])\]

其中 \(ans_{case}\) 表示没加入根时子树的颜色情况为 \(case\) 时的最小代价。

那 \(ans_{case}\) 答案怎么求呢?首先如果儿子的子树大小为偶,直接加入贡献即可,因为此时加入不会对颜色的情况造成影响,然后对奇数大小的儿子贪心取最优即可,贡献可以表示为:

\[ans_{R-B=x}=\sum_{v\in son[偶]_u}f[0][v]+min\{\sum_{i=1,v\in son[奇]_u}^{kr}f[1][v]+\sum_{i=1,v\in son[奇]_u}^{kb}f[2][v]\} \]

其中 \(kr+kb=sum_{奇子树个数},kr-kb=x\)。

怎么贪心取最优?我们假设先全选蓝色,然后把红色减蓝色的贡献加入优先队列,取最小的即可。

\(Code\)


int n;          // 0:R=B 1:R>B 2:R<B
ll a[1000006],b[1000006],f[3][1000006],siz[1000006];
vector<int>F[1000006];
void dfs(int x,int fa){
    priority_queue<ll,vector<ll>,greater<ll> >Q;
    siz[x]=1;
    ll ans=0;
    for(int y:F[x]){
        if(y==fa)continue;
        dfs(y,x);
        siz[x]+=siz[y];
        if(siz[y]&1){
            ans+=f[2][y];
            Q.push(f[1][y]-f[2][y]);
        }else ans+=f[0][y];
    }
    if(!Q.size()){
        f[1][x]=ans+a[x],f[2][x]=ans+b[x];
        return;
    }
    if(siz[x]&1){
        //奇数的时候,一定有偶数个子树不是偶siz
        //要么是少两个b然后选b,要么少两个a选a,要么不多不少选a或者选b
        //两种答案,各分两种情况讨论。
        int cnt=Q.size()>>1;//枚举选几个a,不减1是持平的
        cnt--;
        while(cnt--){
            ans+=Q.top(),Q.pop();
        }
        f[2][x]=min(ans+a[x],ans+Q.top()+b[x]);
        ans+=Q.top();
        Q.pop();
        f[1][x]=min(ans+a[x],ans+Q.top()+b[x]);
    }else{
        //偶数的时候,一定有奇数个子树不是偶siz
        //要么是少a选b,要么是少b选a
        int cnt=Q.size()>>1;//枚举选几个a,这里是先少选a的
        while(cnt--){
            ans+=Q.top(),Q.pop();
        }
        f[0][x]=min(ans+a[x],ans+Q.top()+b[x]);
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read(),b[i]=read();
    }
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        F[u].push_back(v);
        F[v].push_back(u);
    }
    dfs(1,0);
    ll ans;
    if(n&1)ans=min(f[1][1],f[2][1]);
    else ans=f[0][1];
    cout<<ans;
}

一开始其实思路跟这个差不多,但是由于我不想开三维,就开了一维然后分奇偶讨论,天真地以为奇数大小的子树中除根的节点必然满足红色等于蓝色,然后方法差不多,直接把奇数情况的 \(a[v]-b[v]\) 扔进优先队列了,没想到其实与根的颜色关系不大,要看的是子树的颜色,然后就全 WA 了(邪恶的捆绑测试点)。

下次讨论 dp 的时候不要想当然,要关注整体的贡献而不是个体的贡献(根节点的颜色不影响整体,看得是子树多的颜色),多思考,反思,看题也要仔细(一开始以为不算根)!!

C

分类讨论大挂了,不过问题不大,其实也想到了一些构造思想,但是没想到那么显然。

题意:

给定一个 \(n\) 个点的完全图,你要给每条边定一个方向,使得得到的有向完全图(又称竞赛图)的三元环个数最多。(\(n\le 1000\))

思路

观察图中任意三个点的关系,发现要么是三元环,要么就是一个点指向另外两个点。三元环的个数不好算,但是第二个的个数好算,我们令 \(i\) 点的出度为 \(deg_i\),那么非三元环的个数就是 \(\sum\limits_{i=1}^{n}\binom{deg_i}{2}\),三元环的个数即为:\(\binom{n}{3}-\sum\limits_{i=1}^{n}\binom{deg_i}{2}\)。而:

\[\sum\limits_{i=1}^{n}\binom{deg_i}{2}=\frac{1}{2}\sum\limits_{i=1}^{n}(deg_i-1)deg_i=\frac{1}{2}\sum\limits_{i=1}^{n}deg_i^2-\frac{1}{2}\sum\limits_{i=1}^{n}deg_i=\frac{1}{2}\sum\limits_{i=1}^{n}deg_i^2-\frac{n(n-1)}{4} \]

所以只需要最小化 \(\sum\limits_{i=1}^{n}deg_i^2\) 即可,观察到一个点的出度与入度越接近,这个值就越小。

提供两个构造方法:

  • 奇数个点时显然每个点可以做到有 \(\frac{n-1}{2}\) 个出度,然后偶数个只需要在奇数个的基础上加一个点平摊出度即可

\(Code_1\)

    m=n;
    if(n+1&1)m--;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            if(i!=j&&((m-i+j)<=(m-1>>1)||(j-i<=(m-1>>1)&&j>i)))
                a[i][j]=1;
        }
    }
    if(n+1&1){
        for(int i=1;i<=(m>>1);i++)a[i][n]=1;
        for(int i=(m>>1)+1;i<=m;i++)a[n][i]=1;
    }
    for(int i=1;i<=n;i++,puts("")){
        for(int j=1;j<=n;j++){
            printf("%d",a[i][j]);
        }
    }
  • 就是隔一个连一个,这样基本可以平摊出度。

\(Code_2\)

    int n=read();
    for(int i = 1;i<=n;i++) for(int j = i+1,t=1;j<=n;j++) a[i][j]=t,t=1-t;
    for(int i = 1;i<=n;i++) for(int j = i+1,t=0;j<=n;j++) a[j][i]=t,t=1-t;
    for(int i = 1;i<=n;i++){for(int j = 1;j<=n;j++)cout<<a[i][j];cout<<"\n";}

然后我目前听得懂的题目就这么多了,把 D 放出来让大佬们思考一下。

D

问题描述

给定一个 \(n\) 个点的树 \(G\),称它的一个子图 \(G′\) 为“子树”当且仅当 \(G=G′\),或者存在 \(G\) 上的一条边,将 \(G\) 断掉这条边之后 \(G′\) 是分成的两个连通块之一。

对于所有的 \(k=1,2,3\dots n\),求从这棵树中等概率选出 \(k\) 个不同的点,包含它们的最小的“子树”的大小的期望模 \(998244353\) 的值。

\(3\le n\le 7000\)。

样例

in:

5
1 2
1 3
2 4
2 5

out

798595484
499122180
4
199648875
5

思路

我只想到了 \(n\le 15\) 时,枚举断掉的边和选中的点,然后搜索即可。

标签:le,ll,int,题解,sum,集训,ans,Day19,deg
From: https://www.cnblogs.com/NBest/p/17686910.html

相关文章

  • 暑假集训 Day17 模拟赛题解
    2023-08-0318:18:03前言好家伙,很少完整订正一场比赛,可能是因为这个比赛相对来说确实不难吧(至少正解不难)。总结与反思这场比赛其实没有我想象的那么难,只是觉得题目可能不简单,就没有往简单的思路想,反而是被之前讲过的题疑惑,以为要用到一些很奇特的算法,结果打完以后看了题解再结......
  • UVA10368 题解
    2023-08-0615:18:08solution双倍经验这种有限轮游戏的博弈通常都是有两种状态,必胜态和必败态。对于必胜态,指的是从它可以转移到必败态。对于必败态,指的是从它不论如何只能转移到必胜态。对于每个玩家都采取最优策略的有限游戏,我们通常只需要关注必胜态与必败态之间的转移即......
  • 【题解】AtCoder Regular Contest 162
    A.EkidenRace题目描述:有\(n\)个人参加了往返赛跑,每个人有一个编号\(1\)到\(n\)。已知以下信息:如果按照往路的成绩排序,那么任何两个人的成绩都不相同。同时第\(i\)个人在往路中排名第\(i\)。如果按照往返的成绩排序,那么任何两个人的成绩都不相同。同时第\(i\)个人......
  • P9189 [USACO23OPEN] Custodial Cleanup G 题解
    Description奶牛旅馆可以被看作一个\(N\)个节点\(M\)条边的无向简单图,其中每个房间有一个颜色\(C_i\),以及一个钥匙,颜色为\(S_i\),FJ最初在\(1\)号节点,手上一把钥匙都没有。FJ可以进行无数次以下操作:捡起当前房间的钥匙。(FJ可以同时手持多个钥匙)将部分或全部手......
  • All Pairs Maximum Flow题解
    前置知识:1.P3376【模板】网络最大流2.P4897【模板】最小割树(Gomory-HuTree)Ebola有一句很著名的话如果你乱搞过了我请你抽烟那么这道题肯定不能普通的dinic直接水过去,不然就不是紫题了,那么直接祭出最小割树,复杂度\(O(Tn^3m)\),但是因为dinic跑不满,所以是可以过的。......
  • [题解] AtCoder Beginner Contest 308 A~G
    AtCoderBeginnerContest308A~GA.NewSchemevoidMain(){vector<i64>a(8);for(auto&x:a)cin>>x;if(!is_sorted(a.begin(),a.end())&&!all_of(a.begin(),a.end(),[](auto&x){returnx%25!=0||!(100......
  • CF1374E2 Reading Books(hard version) 题解
    CF1374E2ReadingBooks(hardversion)这道题是在CF1374E1ReadingBooks(easyversion)的基础上出的,而且仅仅增加了一个\(m\)的限制,下面的做法也是基于简单版的思路而想到的。建议先尝试简单版,然后尝试此题。题意简述:有\(n\)本书,每本书有一个阅读时间\(t_i\),和属性\(......
  • Eclipse里做JBPM工作流gpd.xml中文乱码问题解决
         刚开始接到是做一个简单的文档借阅流程,对于流程定义是采用eclipse中的jbpm插件,但存在一个问题是节点中文命名的在gpd.xml中全部为乱码或根本看不到任何东西。     但是网上有人说没关系,这只是eclipse本身存在的一个bug,在项目所在硬盘目录下打开该文件还是显示正常......
  • 题解 [NOIP2018 提高组] 赛道修建
    题目链接挺综合的一道题目。询问最小值最大,考虑二分最小值,二分上下界是\([最小边权,树的直径]\),但是为了方便我们直接设为\([1,5\times10^8]\)即可。考虑如何\(check\),可以采用类似树形\(dp\)的方式进行贪心。对于节点\(u\)的子树,\(u\)内部的点显然可以构成几条链,同......
  • 【PCL】使用kdtree时pop_t报错问题解决
    问题描述在使用kdtree时,遇到的报错,具体报错信息如下:提示找不到pop_t,点击错误信息会进入到dist.h文件中问题解决解决办法很简单,在这里简单总结一下进入dist.h文件中,将下面这行代码,应该是在源文件的503行:typedefunsignedlonglongpop_t;具体位置如下图所示:将这行代码......