CodeForces Round #630 (Div. 2) (E、F


E. Height All the Same

题意:

给n和m,表示有一个n*m的矩阵a

给l和r,表示a(i,j)的取值范围在[l,r]呢内
最开始,你可以指定每一个格子a(i,j)为[l,r]为某个值

然后有两种操作:
1.将某一个格子的值加2
2.将相邻的两个格子都加上1

问能构造出多少种初始情况,使得在多次操作之后能让整个矩阵的值相等
答案对998244343取模

数据范围:1<=n,m,l,r<=1e9

解法:

第一个操作一定能让两个奇偶性相同的格子达到同一个数值,因此只需要让所有格子奇偶性相同即可
而第二个操作可以让两个相邻的格子的奇偶性同时翻转

看到别人题解里有一个很好理解的方法:
两个奇偶性不同的格子翻转,可以看成是一个格子的1走到了另一个格子
那么一个1只要能走到另外一个1旁边,他们最后就可以一起变为0
不单单1走到1,0走到0也可以。

那么只要矩阵中存在偶数个1或者偶数个0,
那么一定能通过这种走动使得偶数部分两两消去,最后全部变为另外一个种

如果n*m是奇数的话,不管怎么分配格子的奇偶性,一定都有一方是偶数个,因此每个格子可以随便发
方案数为(r-l+1)nm

如果n*m是偶数的话,就用组合数学算取偶数个的方案数
设x为[l,r]中奇数个数,y为[l,r]中偶数个数,那么:
答案为C(nm,0)x0ynm+C(nm,2)x2ynm-2+…+C(nm,nm)xnmy0
直接算不好算,可以用这个式子构造出((x+y)nm+(x-y)nm)/2,这样就好算了

也可以用dp算,设d(i,0)表示前i个格子中奇偶性为偶数的格子的个数为偶数个的方案数
d(i,1)表示前i个格子中奇偶性为偶数的格子的个数为奇数个的方案数
则d(i,0)=d(i-1,1)x(eve),d(i,1)=d(i-1,0)x(odd),其中eve和odd是取值范围内偶数和奇数的个数
虽然i很大,但是这个式子可以用矩阵快速幂加速转移

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ppow(int a,int b,int mod){
    int ans=1%mod;a=(a%mod+mod)%mod;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
//
const int mod=998244353;
signed main(){
    int n,m,l,r;cin>>n>>m>>l>>r;
    n*=m;//n*m;
    if(n%2==1){
        cout<<ppow(r-l+1,n,mod)<<endl;
    }else{
        int odd=(r+1)/2-((l-1)+1)/2;
        int eve=(r-l+1)-odd;
        int ans=(ppow(odd+eve,n,mod)+ppow(odd-eve,n,mod))*ppow(2,mod-2,mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}

F. Independent Set

题意:

给一颗n个节点的树,问这棵树的每个非空边诱导子图的独立集种类的和。

边诱导子图:选择原图中的一些边,那么原图中这些边的连接的顶点也在这个边诱导子图中
点诱导子图:选择原图中的一些点,那么原图中这些点之间的边也在这个点诱导子图中

解法:

将独立集中的点染色,独立集合法的情况为相邻顶点不同时被染色。
问题变为从树中选择若干顶点染色,然后对树进行删边,
使得不存在边连接两个同时染色的顶点,且不存在孤立的被染色的点
统计总方案数

设:
d(u,0)为不染色u的方案数
d(u,1)为染色u的方案数
d(u)为染色u但是删掉了u的所有边的方案数(即不合法方案数)

那么转移方程:
在这里插入图片描述
乘2是因为染色和删边这两种情况都要考虑

最后注意题目要求的是非空边诱导子图,因此答案需要减掉1

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=3e5+5;
const int mod=998244353;
vector<int>g[maxm];
int d[maxm][3];
void dfs(int x,int fa){
    for(int i=0;i<3;i++)d[x][i]=1;
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x);
        d[x][0]=d[x][0]*((d[v][0]*2+d[v][1]*2-d[v][2]+mod)%mod)%mod;
        d[x][1]=d[x][1]*((d[v][0]*2+d[v][1]  -d[v][2]+mod)%mod)%mod;
        d[x][2]=d[x][2]*((d[v][0]  +d[v][1]  -d[v][2]+mod)%mod)%mod;
    }
}
signed main(){
    int n;cin>>n;
    for(int i=1;i<n;i++){
        int a,b;cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,-1);
    cout<<(d[1][0]+d[1][1]-d[1][2]-1+mod)%mod<<endl;
    return 0;
}
/*
d[x][0],点x不染色
d[x][1],点x染色
d[x][2],点x染色但是边全部断开
转移的时候:染色和删边这两种情况都要考虑
*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值