Codeforces Round #630 (Div. 2)

E

题意

一个 n ∗ m n*m nm的矩阵,问能通过选择相邻两格 + 1 +1 +1,或者某个格子 + 2 +2 +2,使得最终所有格子一样多的初始矩阵有多少种。
并且初值在 [ L , R ] [L,R] [L,R]中。

题解

我们可以发现,每次贡献都是 2 2 2,也就是说初始矩阵变成相等之后的总和奇偶性不变。
假设最后是 k k k ( n m ∗ k − t o t ) % 2 = 0 (nm*k-tot)\%2=0 (nmktot)%2=0是必要条件。

当然,我们简单推一推,可以发现这个应该是充要条件。(大胆猜测)
考虑 121 1 21 121,通过 + ( 1 、 2 ) + ( 2 、 3 ) + ( 2 、 2 ) +(1、2)+(2、3)+(2、2) +(12)+(23)+(22),可以实现相同。

然后接下来就是如何计算了,如果 n m nm nm是奇数,那么不论 t o t tot tot奇偶,总有 k k k可以满足。
也就是 ( r − l + 1 ) n m (r-l+1)^{nm} (rl+1)nm
如果是偶数,那么就要保证 t o t tot tot是偶数。

如果 [ L , R ] [L,R] [L,R]中有 a a a个奇数, b b b个偶数,答案是:
∑ i % 2 = = 0 a i ∗ b n m − i + 1 = ( a + b ) n m − ( a − b ) n m 2 \sum\limits_{{i\%2==0}}{a^i*b^{nm-i+1}}=\frac{(a+b)^{nm}-(a-b)^{nm}}{2} i%2==0aibnmi+1=2(a+b)nm(ab)nm
分子第一个是计算所有的 i i i为奇数或偶数,第二个是计算 i i i奇数贡献是 1 1 1,偶数是 − 1 -1 1
n m nm nm是偶数, i i i为偶数的时候, n m − i + 1 nm-i+1 nmi+1是奇数,所以 − 1 -1 1不会被抵消。

最后和能够把 n m − i + 1 nm-i+1 nmi+1为奇数,也就是 i i i为偶数的情况抵消。

当然我代码 a a a b b b反过来了。

#include 
using namespace std;
#define int long long
const int MOD = 998244353;
 
typedef long long ll;
long long quickpow(long long n, long long base) {
    long long res = 1;
    while(n) {
        if(n & 1) {
            res = res * base % MOD;
        }
        n >>= 1;
        base = base * base % MOD;
    //    cout<
    }
    return res;
}//快速幂
 
signed main() {
    ll n,m,l,r;
    cin>>n>>m>>l>>r;
    if(n*m%2)cout<<quickpow(n*m,r-l+1);
    else{
            if(l%2){
                ll a=(r-l+2)/2,b=(r-l+1)/2;
               // cout<
                ll ans=quickpow(n*m,a+b);
               // cout<
                ans+=quickpow(n*m,a-b)%MOD;
                ans%=MOD;
                ans=ans*quickpow(MOD-2,2)%MOD;
                cout<<ans<<endl;
            }
            else{
                ll a=(r-l+1)/2,b=(r-l+2)/2;
                ll ans=quickpow(n*m,a+b)+quickpow(n*m,a-b)%MOD;
                ans%=MOD;
                ans=ans*quickpow(MOD-2,2)%MOD;
                cout<<ans<<endl;
            }
    }
 
}

F

题意

对于所有边集,除了空集,对于每个边集,其关联的点的所有独立集数目,然后把所有边集得到的答案相加。
图是一棵树

题解

参考自大佬

首先如果边集是满的,那么就是:
d p [ u ] [ 0 ] = ∏ d p [ v ] [ 0 ] + d p [ v ] [ 1 ] dp[u][0]=\prod dp[v][0]+dp[v][1] dp[u][0]=dp[v][0]+dp[v][1]
d p [ u ] [ 1 ] = ∏ d p [ v ] [ 0 ] dp[u][1]=\prod dp[v][0] dp[u][1]=dp[v][0]
分别表示对于以 u u u为根的子树, u u u不染色和染色情况下得到的答案。

如果不满,需要考虑断边。
如果 f [ u ] f[u] f[u]表示答案,即 u u u为根的子树的答案。

d p [ u ] [ 0 ] = ∏ d p [ v ] [ 0 ] + d p [ v ] [ 1 ] + f [ v ] dp[u][0]= \prod dp[v][0]+dp[v][1]+f[v] dp[u][0]=dp[v][0]+dp[v][1]+f[v]
表示,不断边子树是任意的,断边之后依然是任意的。
d p [ u ] [ 1 ] = ∏ d p [ v ] [ 0 ] + f [ v ] dp[u][1]=\prod dp[v][0]+f[v] dp[u][1]=dp[v][0]+f[v]
表示,不断边子树只能取 0 0 0,断边之后是任意的
d p [ u ] [ 2 ] = ∏ f [ v ] dp[u][2]=\prod f[v] dp[u][2]=f[v]
表示,把所有边断了之后的答案(提醒,这里断边之后就没有 u u u点了,所以独立集一定不包括 u u u。子树是所有情况相乘)
f [ u ] = d p [ u ] [ 0 ] + d p [ u ] [ 1 ] − d p [ u ] [ 2 ] f[u]=dp[u][0]+dp[u][1]-dp[u][2] f[u]=dp[u][0]+dp[u][1]dp[u][2]
首先该点染色不染色是两种情况相加,但是发现我们计算的时候,考虑了断边不断边的情况,如果只断了某些边,即存在边相连,那么这两种情况是截然不同的,但是全断了之后, u u u点将不被考虑,也就是说该点染不染色对结果没有影响,而我们计算了两次,所以需要减去。

最后答案是 f [ 1 ] − 1 f[1]-1 f[1]1,因为我们考虑了空集,即算上了把边全部去掉的情况。

#include 
using namespace std;

typedef long long ll;
const int mod = 998244353;
const int maxn = 400050;

vector<int>G[maxn];
ll dp[maxn][4];

void dfs(int u,int f){
    dp[u][1]=dp[u][0]=dp[u][2]=1;
    for(auto v:G[u]){
        if(v==f)continue;
        dfs(v,u);
        ll tmp=dp[v][0]+dp[v][1]-dp[v][2];
        tmp=(tmp%mod+mod)%mod;
        dp[u][0]=dp[u][0]*((dp[v][0]+dp[v][1]+tmp)%mod)%mod;
        dp[u][1]=dp[u][1]*((dp[v][0]+tmp)%mod)%mod;
        dp[u][2]=dp[u][2]*tmp%mod;
    }
}

int main(){
    int n;cin>>n;
    for(int i=1;i<=n-1;i++){
        int u,v;scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,-1);
    ll ans=dp[1][0]+dp[1][1]-dp[1][2]-1;
    ans=(ans%mod+mod)%mod;
    cout<<ans<<endl;
}

你可能感兴趣的:(Codeforces Round #630 (Div. 2))