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染色但是边全部断开
转移的时候:染色和删边这两种情况都要考虑
*/