Codeforces Round #791 (Div. 2) 部分题解

A AvtoBus

不难发现如果我们要车尽可能多,那么我们就需要最大化 4 4 4 的数目,尽可能少的情况就是最大化 6 6 6 的数目,我们同时发现 2 2 2 6 6 6 轮车等价于 3 3 3 4 4 4 轮车,有了这两个性质就不难解决。

B Stone Age Problem

考虑两种修改对于答案的影响,由于我们只关注序列权值的总和,我们只要记录什么时候进行了修改,在单点修改时判断下即可。

C Rooks Defenders

不难发现如果要将一个矩阵填满,那么有且仅有所有行均被覆盖或者所有列均被覆盖才合法,所以我们对于行和列分别维护线段树,以行举例,当一行上棋子数目不为零时就 + 1 +1 +1 ,为零时就 − 1 -1 1即可。

D Toss a Coin to Your Graph…

首先如果要让最大值最小,我们可以考虑二分这个最大值 M M M,这样我们对于一个图 G G G 就有了一些权值大于 M M M 的点是不能进行选择的,我们将这些点从图中删去得到一个子图 S S S,这个图上是可以随便走的。问题就转化成了在图 S S S 上去找一条包含点数为 k k k 的路径。
首先如果图 S S S 中有环,那么就一定合法,判环的代码如下:

void findcur(int x){
    col[x]=1;
    for(auto v:e[x]){
        if(!tag[v]) continue;//tag[v]=false当且仅当val[v]>M
        if(col[v]==1){
            flag=true;
            break;
        }
        else if(col[v]==-1) continue;
        else findcur(v);
    }
    col[x]=-1;//表示以x为起点的路径上没有环
}

如果没有环我们就记忆化搜索下最长路径与 k k k 比较即可。

E Typical Party in Dorm

我们首先考虑一个左端点为 l l l,右端点为 r r r 的子串对于答案的贡献:
我们将该字串分为若干点对,其中 l l l r r r 配对, l + 1 l+1 l+1 r − 1 r-1 r1 配对,依次类推。
我们令 S q S_q Sq 表示某次询问给出的字符集。
这样我们考虑其中的一对 a a a b b b
s [ a ] = ? s[a]=? s[a]=? 并且 s [ b ] = ? s[b]=? s[b]=? 时,我们可以任取给出集合中的字符。
s [ a ] = ? s[a]=? s[a]=? 并且 s [ b ] = t ( t ∈ S q ) s[b]=t (t\in S_q) s[b]=t(tSq) 时,那么只有一种合法情况,会产生一个限制条件:集合 S q S_q Sq 中必须含有字符 t t t,反过来同理。
当二者都不为问号时,如果相等则没有影响,如果不等则子串 ( l , r ) (l,r) (l,r) 对答案将不产生贡献。
我们考虑子串以外的字符,不难发现每一个 ? ? ? 都将对于答案产生 ∣ S q ∣ |S_q| Sq 的贡献。
这样我们定义 f f f 为内部产生的方案数, d d d 为外部产生的方案数,对于每个串我们可以确定一个三元组 ( f , d , s t a t e ) (f,d,state) (f,d,state) ,其中 s t a t e state state 为一个二进制数,其第 i i i 位为 1 1 1 表示字符 ′ a ′ + i 'a'+i a+i 是否必须包含于询问集合。
这样我们定义 c [ k ] [ s t a t e ] c[k][state] c[k][state] 表示当限制条件构成的集合大小为 k k k 时,其二进制表示为 s t a t e state state 时的方案数,这个我们可以枚举 l , r , k l,r,k l,r,k 来进行计算。
这样我们考虑我们对于一个询问,其字符集为 Q Q Q,方案数就是:
∑ i ∈ Q c [ b i t c o u n t ( i ) ] [ i ] \sum_{i\in Q} c[bitcount(i)][i] iQc[bitcount(i)][i]
这个我们枚举子集可以 3 α 3^\alpha 3α 进行计算,不过并不能通过本题。
题解给出了一种做法:
我们定义 S ( c u r , i ) = { x ∣ x ∈ c u r 并 且 x ⊕ c u r ≤ 2 i } S(cur,i)=\{ x| x\in cur 并且 x \oplus cur \leq 2^i \} S(cur,i)={xxcurxcur2i}
也就是 x x x c u r cur cur 只有 0 0 0 i i i 位可以不同,并且 x x x c u r cur cur 的子集。
我们有以下转移:
c u r cur cur 的第 i i i 位为 0 0 0 的时候, S ( c u r , i ) = S ( c u r , i − 1 ) S(cur,i)=S(cur,i-1) S(cur,i)=S(cur,i1)
c u r cur cur 的第 i i i 位为 1 1 1 的时候, S ( c u r , i ) = S ( c u r , i − 1 ) ∪ S ( c u r ⊕ 2 i , i − 1 ) S(cur,i)=S(cur,i-1) \cup S(cur \oplus 2^i,i-1) S(cur,i)=S(cur,i1)S(cur2i,i1)
这样就大大的提升了枚举的效率,将复杂度降到了 α 2 α \alpha2^\alpha α2α
代码如下:

for(int k=1;k<M;++k){
        for(int i=0;i<M;++i){
            for(int cur=(1<<M)-1;cur>=0;--cur){
                if(cur&(1<<i)) (F[k][cur]+=F[k][cur^(1<<i)])%=MOD;
            }
        }
    }

预处理后对于每个询问的答案就是 F [ ∣ Q ∣ ] [ s t a t e Q ] F[|Q|][state_Q] F[Q][stateQ],复杂度为 O ( α 2 2 α + α n 2 ) O(\alpha^22^\alpha+\alpha n^2) O(α22α+αn2)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值