题意
给一个图,图上的点可以被染成权值为1,2,3
令边权=两个点的点权和,求令边权为奇数的所有方案数%998244353
题解
首先二分图判定一下,分奇偶层;
奇层染奇数,偶层染偶数;
或奇层染偶数,偶层染奇数。
对于每个连通分量,其方案数为(modpow(2,奇层点数,MOD)+modpow(2,偶层点数,MOD))%MOD
这个modpow是快速幂。
然后整个图的就是所有连通分量的乘积%MOD了。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const int INF=0x3f3f3f3f;
const int maxn=3e5+10;
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<int,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%I64d",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int cnt,head[maxn],color[maxn],shu;
int t,n,m,pos[maxn],neg[maxn],now;
struct edge
{
int to , nxt;
}e[maxn << 1];
void init(int n)
{
for(int i=0;i<n;++i)
color[i]=0,head[i]=-1;
cnt=0;
shu=1;
}
void add(int u , int v){
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
ll modpow(ll x,ll n,ll mod)
{
if(n==0)return 1;
ll res=modpow(x,n/2,mod),ans=res*res;
if(ans>=mod)ans%=mod;
if(n&1)ans=ans*x;
if(ans>=mod)ans%=mod;
return ans;
}
bool judge(int u , int c){
color[u] = c;
if(c > 0) pos[c]++;
else {
int p = -c;
neg[p]++;
}
for(int i = head[u]; ~i ; i = e[i].nxt){
int v = e[i].to;
if(color[v] == c) return 0;
if(color[v] == 0 && !judge(v,-c))
return 0;
}
return 1;
}
ll solve(){
ll ans = 1;
for(int i = 0 ;i < n; i++){
if(color[i] == 0){
pos[shu] = 0;
neg[shu] = 0;
if(!judge(i,shu)) return 0;
shu++;
}
}
rep(i,1,shu-1){
ll tmp1 = modpow(2,pos[i],MOD);
ll tmp2 = modpow(2,neg[i],MOD);
ll q = tmp1 + tmp2;
if(q >= MOD) q %= MOD;
ans = ans * q;
if(ans >= MOD) ans %= MOD;
}
return ans ;
}
int main()
{
cin >> t;
while(t--){
cin >> n >> m;
init(n);
rep(i,0,m-1){
int u , v;
cin >> u >> v;
u-- , v --;
add(u,v);
add(v,u);
}
cout << solve() << endl;
}
return 0;
}
题意:1 2 3 ,每个点给一个值,要求相邻两点权值加和为奇数,求方案数
题解:假设有两个数1 2的话,很明显,如果存在合理方案,那么就肯定为两种,1和2换一下即可,因为这里多了一个3,所以,
如果权值为奇数的为cnt1个,偶数的cnt2个,此时方案数为2^cnt1, 然后奇偶一换,方案数为2^cnt2
所以方案总数为2cnt1+2cnt2,因为没说是所有的点联通,所以每块乘起来即可,若遇到不符合标记一下即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=3e5+10;
#define pb push_back
int n,m,vis[N];
vector<int> v[N];
int cnt1,cnt2;
int flag;
void dfs(int u)
{
if(vis[u]==1) cnt1++;
else cnt2++;
for(int i=0;i<v[u].size();i++)
{
int to=v[u][i];
if(vis[to])
{
if(vis[to]==vis[u]) flag=1; // 遇见不符合 标记
}
else
{
vis[to]=3-vis[u];
dfs(to);
}
}
}
ll ksm(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)vis[i]=0,v[i].clear();
flag=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
v[x].pb(y);
v[y].pb(x);
}
ll ans=1;
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
vis[i]=1;
cnt1=cnt2=0;
dfs(i);
if(flag) break;
ans=(ans*(ksm(2,cnt1)+ksm(2,cnt2))%mod)%mod;
}
if(flag) cout<<"0\n";
else cout<<ans<<endl;
}
return 0;
}
题意概括: 给一个无向图,要求在每个结点填入 1,2,3
三个数的其中一个,如果每条边相连的两个结点的权值之和为奇数,则当前的填数方案是满足条件的;询问有多少种填数方案,如果不存在则输出0;
解题思路: 因为只有 1, 2, 3 三个数,其中有两个奇数,
一个偶数。为了满足题目的条件,我们的填入规则肯定是在一条路径上奇偶奇偶…这样填入数字,保证相连两点的和为奇数。那么就有该路径是先填奇数还是先填偶数之分了:
如果是在该路径上的第一点填入奇数,那么由这个点出发的路径的偶数点肯定是要填偶数,那么我们统计填入奇数的点(即奇数点)的个数 p
,每个点可填两种数,方案有 2 的 p 次方种。如果在该路径的第一点填入偶数,那么偶数点要填奇数(1或者3),统计填入奇数的点(即偶数点)的个数 x,每个点有两种选择,方案数为 2 的 x
次方。那么总的方案数就是上面两种情况的方案数之和。
由此,我们发现统计路径的奇数点和偶数点,分别以他们求2次幂之和,就是以当前点出发所能经过路径的方案数了。枚举一遍起点,求出总的方案数就是答案。
那么无解的情况呢,就是存在起点和终点奇偶性相同的环(DFS过程中判断一下即可)。
注意点:
一、2次幂可先预处理
二、一开始敲得静态邻接表版本要注意初始化得方式 for循环可过,效率客观(比vector版本快些)。memset初始化超时。
三、vector版本的for循环初始化即可。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const LL MOD = 998244353;
int N, M, p, s;
LL ans;
const int MAXN = 3e5+10;
int book[MAXN];
LL pw[MAXN+1];
bool con;
//bool vis[MAXN];
struct EDGE
{
int v, nxt;
}edge[MAXN<<1];
int head[MAXN], cnt;
void add(int u, int v)
{
edge[cnt].v = v;
edge[cnt].nxt = head[u];
head[u] = cnt++;
}
void init()
{
// memset(book, -1, sizeof(book));
// memset(head, -1, sizeof(head));
cnt = 0;
con = true;
ans = 1LL;
}
void dfs(int now, int flg)
{
if(flg == 1) p++;
else s++;
book[now] = flg;
int v;
for(int i = head[now]; i != -1; i = edge[i].nxt){
v = edge[i].v;
if(book[v] == -1){
dfs(v, 1-flg);
}
else if(book[v] == flg) {con = 0; return;}
}
}
int main()
{
int T_case, u, v;
scanf("%d", &T_case);
pw[0] = 1;
for(int i = 1; i < MAXN; i++){
pw[i] = (pw[i-1]*2)%MOD;
}
memset(head, -1, sizeof(head));
memset(book, -1, sizeof(book));
while(T_case--)
{
init();
scanf("%d%d", &N, &M);
for(int i = 1; i <= M; i++){
scanf("%d %d", &u, &v);
add(u, v);
add(v, u);
}
for(int st = 1; st <= N; st++){
if(book[st] != -1) continue;
else if(book[st] == -1){
p = 0, s = 0;
dfs(st, 1);
ans = ans*(pw[p]+pw[s])%MOD;
}
}
if(con) printf("%I64d\n", ans);
else printf("0\n");
for(int i = 0; i <= N; i++){
head[i] = -1;
book[i] = -1;
}
}
return 0;
}