A - Replace Digits
简要题意
给定一个长为 \(n\) 的字符串 \(a\)。\(m\) 次操作,第 \(k\) 次给定一个字符 \(b_k\),你需要选择一个 \(i \in [1, n]\) 并将 \(a_i\) 替换成 \(b_k\)。
求最后能得到的所有字符串中,字典序最大的是什么。
\(1 \leq n, m \leq 10^6\),\(a_i, b_k\) 均为非零数字。
操作的顺序实际上没有太大关系,因此显然有一个贪心做法:从前往后依次考虑 \(a\) 的每一位,记此时 \(b\) 中还没用过的最大字符为 \(c\),如果 \(c > a_i\) 就将 \(a_i\) 替换成 \(c\)。由于询问的是字典序,这么贪心显然是正确的。
不过虽然操作顺序没有太大关系,但它会带来一个特殊限制,即字符串 \(a\) 中至少有一个字符为 \(b_m\)。这个特判一下即可,如果最终 \(a\) 序列中没有任何一个字符为 \(b_m\),那么就强制把 \(a_n\) 替换成 \(b_m\)。
时间复杂度为 \(\Theta(n + m)\) 或 \(\Theta(n + m \log m)\),瓶颈在给 \(b\) 中的字符排序。
代码
#include <cstdio>
#include <queue>
#include <utility>
#define x first
#define y second
using namespace std;
const int N=(int)1e6+3;
int n,m; char a[N],b[N]; bool bj[N];
priority_queue<pair<char,int> > hp;
int main(){
// freopen("digit.in","r",stdin);
// freopen("digit.out","w",stdout);int i; bool ned=1;scanf("%d%d%s%s",&n,&m,&a[1],&b[1]);for(i=1;i<=m;i++) hp.emplace(b[i],i);for(i=1;i<=n&&!hp.empty();i++){if(hp.top().x<a[i]) continue;if(hp.top().y==m) ned=0;if(hp.top().x>a[i])a[i]=hp.top().x,bj[hp.top().y]=1,hp.pop();}if(ned) a[n]=b[m];puts(&a[1]);
// fclose(stdin);
// fclose(stdout);return 0;
}
B - XOR = MOD
简要题意
给定 \(n, m\),称一个正整数 \(x\) 是“协调的”当且仅当 \(x \oplus n = x \bmod n\),其中 \(\oplus\) 表示按位异或,\(\bmod\) 表示取模。
请你找出第 \(m\) 小的协调数,或报告不存在。
多测,\(1 \leq t \leq 2 \times 10^5\),\(1 \leq n, m \leq 10^9\)。
条件看起来很怪异,我们需要想办法把它转化成正常的表述。
首先,我们发现必有 \(x \geq n\),否则 \(n = x \oplus (x \oplus n) = (x \bmod n) \oplus (x \oplus n) = 0\) 与题目矛盾。其次,根据取模的性质,有 \(0 \leq x \oplus n < n\),这告诉我们在二进制下,\(x\) 与 \(n\) 位数相同。结合以上两点,我们可以推出 \(n \leq x < 2n\),于是上述条件可以表示为 \(x \oplus n = x - n\)。
我们知道,异或相当于不进位的加法、不退位的减法,而这两个数进行减法无论退不退位得出的结果都是一样的,这告诉我们相减的过程中不存在退位,即 \(n\) 为 \(1\) 的位 \(x\) 也必须为 \(1\)。结合上面的结论 \(x\) 与 \(n\) 位数相同,手模一下即可发现这两个条件是充要的。于是题目转化为:
首先将 \(n\) 转化为一个不含前导 \(0\) 的二进制数,你可以选择其中一些值为 \(0\) 的数位并替换为 \(1\)(也可以不选),求所有方案中得到的第 \(m\) 小的二进制数。
于是问题就简单了。设 \(n\) 的二进制表示中有 \(p\) 个 \(0\),只需将 \(m - 1\) 转化为一个 \(p\) 位的二进制数,并依次填入值为 \(0\) 的数位即可。时间复杂度为 \(\Theta(T \log n)\)。
代码
#include <cstdio>
const int N=30;
int p,a[N];
int main(){
// freopen("compatible.in","r",stdin);
// freopen("compatible.out","w",stdout);int i,t,n,m;for(scanf("%d",&t);t>0;t--){scanf("%d%d",&n,&m),m--,p=0;for(i=0;(1<<i)<=n;i++)if(!(n>>i&1)) a[p++]=i;if(m>=(1<<p)) puts("-1");else{for(i=0;i<p;i++)if(m>>i&1) n|=1<<a[i];printf("%d\n",n);}}
// fclose(stdin);
// fclose(stdout);return 0;
}
C - A^n - 1
简要题意
给定正整数 \(n\),你需要构造两个正整数 \(a, m\),满足 \(1 \leq a, m \leq 10^{18}\),且 \(n\) 是 \(a\) 在模 \(m\) 意义下的阶。
多测,\(1 \leq t \leq 10^4\),\(1 \leq n \leq 10^9\)。
感觉这种题完全不是人能想到的啊……
首先你需要写一个暴力,随便构造一个满足 \(n \mid \varphi(m)\) 的正整数 \(m\),然后类似于求原根的过程,从小到大枚举每个 \(a\) 是否合法。然后这个程序连样例都跑不动,但你惊讶地发现:
\({(n + 1)}^n \equiv 1 \pmod {n^2}\) | \({(n + 1)}^{n^2} \equiv 1 \pmod {n^3}\) | \({(n + 1)}^{n^3} \equiv 1 \pmod {n^4}\) | \({(n + 1)}^{n^4} \equiv 1 \pmod {n^5}\) | \({(n + 1)}^{n^5} \equiv 1 \pmod {n^6}\) |
---|---|---|---|---|
\(3^2 \equiv 1 \pmod 4\) | \(3^4 \equiv 1 \pmod 8\) | \(3^8 \equiv 1 \pmod {16}\) | \(3^{16} \equiv 1 \pmod {32}\) | \(3^{32} \equiv 1 \pmod {64}\) |
\(4^3 \equiv 1 \pmod 9\) | \(4^9 \equiv 1 \pmod {27}\) | \(4^{27} \equiv 1 \pmod {81}\) | \(4^{81} \equiv 1 \pmod {243}\) | \(4^{243} \equiv 1 \pmod {729}\) |
\(6^5 \equiv 1 \pmod {25}\) | \(6^{25} \equiv 1 \pmod {125}\) | \(6^{125} \equiv 1 \pmod {625}\) | \(6^{625} \equiv 1 \pmod {3125}\) | \(6^{3125} \equiv 1 \pmod {15625}\) |
直接把 \(a = n + 1, m = n^2\) 交上去便获得了 AC!
还是来想想这是为什么。事实上,由二项式定理,有
代码
#include <cstdio>
int main(){
// freopen("constraint.in","r",stdin);
// freopen("constraint.out","w",stdout);int t,n;for(scanf("%d",&t);t>0;t--){scanf("%d",&n);printf("%d %lld\n",n+1,(long long)n*n);}
// fclose(stdin);
// fclose(stdout);return 0;
}
D - Moving Pieces on Graph
简要题意
给定一张 \(n\) 个点 \(m\) 条边的简单无向连通图,点和边均从 \(1\) 开始编号。有两枚棋子 A 和 B,初始时分别在点 \(S\) 和 \(T\)。
你可以进行任意多次操作,每次操作你可以选择一枚棋子,并将其移动到与之相邻的某个结点上。你的目标是将 A 移动到 \(T\)、将 B 移动到 \(S\),你需要求出达到目标的最小操作次数。如果无解,输出 \(-1\)。
\(2 \leq n \leq 2 \times 10^5\),\(n - 1 \leq m \leq \min\{\frac{n(n - 1)}{2}, 2 \times 10^5\}\),\(S \neq T\)。
首先发现如果 \(S\) 到 \(T\) 的最短路径上(不包括 \(S, T\))存在度数大于等于 \(3\) 的点,那么答案至多为 \(2x + 2\),其中 \(x\) 是最短路径的长度。具体构造方法为:记该点为 \(P\),那么 \(P\) 一定连向了某个最短路之外的点 \(Q\),于是先将 A 沿最短路径移到 \(P\) 再移到 \(Q\),再将 B 沿最短路径从 \(T\) 移到 \(S\),最后将 A 移回 \(P\) 再沿最短路径移到 \(T\)。此时总共经过了两遍最短路径外加 \(PQ\) 这条边两次,总共走了 \(2x + 2\) 步。
由最短路径的性质,显然答案至少为 \(2x\)。于是我们只需要关心最短路上存在度数至少为 \(3\) 的点时,答案是否可能为 \(2x\) 或 \(2x + 1\)。这个只需求一遍非严格次短路,判断次短路的长度是否为 \(x\) 或 \(x + 1\),容易发现此时求出的次短路必然至少有一个点不在最短路上。事实上这种做法不仅限于“最短路上存在度数至少为 \(3\) 的点”这个条件,只要求出的次短路长度不超过 \(x + 1\) 都是合法的。这种情况下问题就解决了。
现在剩下的情况为:最短路上除端点外所有点度数均为 \(2\),且次短路的长度至少为 \(n - 2\)。此时最短路相当于从 \(S\) 到 \(T\) 的一条链,且除端点外链上的每个点都不与链外的点相连。因此一枚棋子的移动方案要么包含整条最短路径,要么与最短路径只在端点处相交。由调整法可知,A 和 B 中至少有一枚棋子的移动方案包含最短路径。不妨令这枚棋子是 A,那么答案有以下两种可能:
-
B 的移动方案与最短路径只在端点处相交。那么 A 的移动路径恰为刚才求出的最短路径,B 的移动路径为将整个图扣去刚才求出的最短路径上的边后,剩余图的最短路径。记对 B 求出的最短路径长度为 \(y\),此时答案为 \(x + y\)。
-
B 的移动方案也包含最短路径。根据上面的性质,显然对于任意时刻,A 和 B 同时在非端点的最短路径上是不优的。那么最优方案一定形如:
- 找到一个度数至少为 \(3\) 的点 \(P'\),记 \(S\) 到 \(P\) 的某条路径为 \(l\),记 \(E, F\) 为两个不在 \(l\) 上且与 \(P\) 相邻的结点。
- 将 A 沿 \(l\) 走到 \(P\),再沿边走到 \(E\)。
- 将 B 沿最短路径走到 \(S\),再沿 \(l\) 走到 \(P\),然后沿边走到 \(F\)。
- 将 A 沿边走到 \(P\),再沿 \(l\) 走到 \(S\),最后沿最短路径走到 \(T\)。
- 将 B 沿边走到 \(P\),再沿 \(l\) 走到 \(S\)。
在上述过程中把 A 和 B、\(S\) 和 \(T\) 交换也可以。记路径 \(l\) 的长度为 \(z\),此时答案为 \(2x + 4z + 4\)。我们只需要求出 \(z\) 的最小值,这个只需要求一遍从 \(S\) 或 \(T\) 出发到每个点的最短路,然后枚举每个度数至少为 \(3\) 的点,求出这些点的最短距离的最小值即可。
根据上面的情况分类讨论即可。由于边权均为 \(1\),求最短路和次短路均可以使用 bfs 实现,时间复杂度为 \(\Theta(n + m)\)。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#define x first
#define y second
using namespace std;
const int N=200003;
int n,m,deg[N],dis1[N][2],dis2[N],pre[N],la[N];
int len_list=0,e[N*2],ne[N*2],h[N]; bool ban[N*2];
void add_once(int a,int b){e[len_list]=b;ne[len_list]=h[a];h[a]=len_list++;
}
void add_twice(int a,int b){add_once(a,b);add_once(b,a);
}
void bfs1(int S){int i,s1; bool s2;queue<pair<int,bool> > dl;dis1[S][0]=0,dl.emplace(S,0);while(!dl.empty()){s1=dl.front().x,s2=dl.front().y,dl.pop();for(i=h[s1];i>=0;i=ne[i])if(dis1[e[i]][0]==-1){dis1[e[i]][0]=dis1[s1][s2]+1;dl.emplace(e[i],0);pre[e[i]]=s1,la[e[i]]=i;}else if(dis1[e[i]][1]==-1){dis1[e[i]][1]=dis1[s1][s2]+1;dl.emplace(e[i],1);}}
}
void bfs2(int S){int i,s1; queue<int> dl;dis2[S]=0,dl.push(S);while(!dl.empty()){s1=dl.front(),dl.pop();for(i=h[s1];i>=0;i=ne[i])if(!ban[i]&&dis2[e[i]]==-1){dis2[e[i]]=dis2[s1]+1;dl.push(e[i]);}}
}
int main(){
// freopen("graph.in","r",stdin);
// freopen("graph.out","w",stdout);int i,x,y,S,T,ans1,ans2=-1;scanf("%d%d%d%d",&n,&m,&S,&T);memset(h,-1,sizeof h);for(i=1;i<=m;i++){scanf("%d%d",&x,&y);add_twice(x,y);deg[x]++,deg[y]++;}memset(dis1,-1,sizeof dis1),bfs1(S);if(dis1[T][1]==-1){puts("-1"); return 0;}if(dis1[T][1]<dis1[T][0]+2){printf("%d\n",dis1[T][0]+dis1[T][1]); return 0;}for(i=pre[T];i!=S;i=pre[i])if(deg[i]>2) break;if(i!=S){printf("%d\n",dis1[T][0]*2+2); return 0;}for(i=T;i!=S;i=pre[i]) ban[la[i]]=ban[la[i]^1]=1;memset(dis2,-1,sizeof dis2),bfs2(S);ans1=(dis2[T]==-1)?-1:(dis1[T][0]+dis2[T]);for(i=1;i<=n;i++)if(dis2[i]>=0&°[i]>=3)ans2=(ans2==-1)?dis2[i]:min(ans2,dis2[i]);memset(dis2,-1,sizeof dis2),bfs2(T);for(i=1;i<=n;i++)if(dis2[i]>=0&°[i]>=3)ans2=(ans2==-1)?dis2[i]:min(ans2,dis2[i]);if(ans2>=0) ans2=ans2*4+dis1[T][0]*2+4;if(ans1>=0&&ans2>=0) printf("%d\n",min(ans1,ans2));else printf("%d",max(ans1,ans2));
// fclose(stdin);
// fclose(stdout);return 0;
}
E - Unfair Game
简要题意
有 \(n\) 个袋子,第 \(i\) 个袋子里面装有 \(a_i\) 个金币和 \(b_i\) 个银币。另外给定两个参数 \(x, y\)。
甲和乙两人正在用这 \(n\) 个袋子玩游戏。初始时,你会从中挑选一些袋子分配给甲(可以不选,也可以全部选完),并将剩余的袋子分配给乙。然后从甲开始,两人轮流执行以下过程:
- 当前玩家从他拥有的袋子中选取一个,袋子中至少要装有一枚硬币。然后从以下两个操作中选择恰好一个操作来执行:
- 从袋子中扔掉一枚金币,并向袋子中加入若干银币。加入银币的数量为:如果当前玩家是甲就加入 \(x\) 枚,否则加入 \(y\) 枚。这个操作能够执行当且仅当该袋子中有至少一枚金币。
- 从袋子中扔掉一枚银币。这个操作能够执行当且仅当该袋子中有至少一枚银币。
- 然后,将这个袋子递给另一名玩家。
无法继续进行操作的人失败,另一个人胜利。
你需要求出初始时有多少种分配袋子的方案,使得最终甲能够获胜。答案对 \(998244353\) 取模。
\(1 \leq n \leq 2 \times 10^5\),\(1 \leq x, y \leq 10^9\),\(0 \leq a_i, b_i \leq 10^9\)。
先考虑只有一个袋子的时候怎么办。此时问题可以拓展为:给定 \(x, y, a, b\),判断甲先手和乙先手两种情况到底谁胜利。手模一下即可发现,由于操作轮流进行且 \(x \neq y\),最后谁胜只和奇偶性以及哪方先手有关。不难发现这又需要分类讨论,先从最简单的情况入手。
- 如果 \(x, y\) 均为奇数,那么银币数量的奇偶性不发生变化。因此如果 \(b\) 是奇数那么先手必胜,否则后手必胜。
- 如果 \(x, y\) 均为偶数,那么硬币总数的奇偶性不发生变化。因此如果 \(a + b\) 是奇数那么先手必胜,否则后手必胜。
- 如果 \(x\) 为奇数、\(y\) 为偶数,此时哪方必胜看起来难以判断。不妨从更小的情况开始判断。
- 如果 \(a = 0\),显然 \(b\) 是奇数时先手必胜,\(b\) 为偶数时后手必胜。
- 如果 \(a = 1\),此时乙操作金币会改变银币数量的奇偶性,而甲不会。感觉依然难以判断,不妨按哪方先手和 \(b\) 的奇偶性继续判断。
- 甲先手且 \(b\) 为奇数。此时甲只需在第一次操作中去掉金币、加入 \(x\) 枚银币,使银币数量变成偶数。后面每次轮到甲时必然还有奇数枚银币,因此甲永远可以操作,最后甲必胜。
- 甲先手且 \(b\) 为偶数。与上面相反,此时甲只能操作银币,否则他必输。轮到乙时有奇数枚银币,乙的策略为:始终操作银币。这样递归下去,每次轮到甲时都有偶数枚银币,甲只能操作银币,因为一旦把操作金币的机会用掉后面就必输;每次轮到乙时都有奇数枚银币,因此乙永远可以操作银币。若乙始终会操作银币,最后第一个无法操作银币的人是甲,此时甲只能操作金币,然后双方轮流操作银币。由于 \(x\) 是奇数,轮到乙时必然有奇数枚银币,因此乙永远可以操作银币,最后乙必胜。
- 乙先手且 \(b\) 为奇数。此时乙只需继续使用上一种情况的策略——每次都操作银币,就可以达到和上一种情况相同的过程,最后乙必胜。
- 乙先手且 \(b\) 为偶数。由于乙操作金币会改变银币数量的奇偶性,乙只需在第一次操作时操作金币,后面轮到甲时必然还剩偶数枚银币,轮到乙时还剩奇数枚,最后乙必胜。
- 总结一下,\(b\) 为奇数时先手必胜,\(b\) 为偶数时乙必胜。
- 如果 \(a \geq 2\),受上述分讨的启发,由于乙操作金币会改变银币数量的奇偶性,而甲不会,因此乙的策略为:第一次操作时判断此时银币数量的奇偶性,若为奇数则操作银币,否则操作金币;此后一直操作银币。因此轮到乙时必然还剩奇数枚金币,乙永远可以操作,最后乙必胜。
- 如果 \(x\) 为偶数、\(y\) 为奇数,那么甲乙的状态与上面相反,先后手的状态不变,有:
- 如果 \(a = 0\),那么 \(b\) 是奇数时先手必胜,\(b\) 为偶数时后手必胜。
- 如果 \(a = 1\),那么 \(b\) 为奇数时先手必胜,\(b\) 为偶数时甲必胜。
- 如果 \(a \geq 2\),那么甲必胜。
上面是只有一个袋子的情况,可事实上有很多袋子,怎么办呢?事实上,可以发现一个袋子只有在被操作的时候它的主人才会变,因此每个袋子本质上是独立的。在题目给定的条件下,根据上面的方法,我们可以对每一个袋子求出在每一方先手的时候,哪一方会胜利。
显然对于一种方案,我们可以调整操作的顺序,使得对同一个袋子的操作被调整到一起。这本质上相当于:对每一个袋子而言,先手胜利相当于下一个袋子的先手转交给另一方,先手失败相当于下一个袋子的先手仍然是当前这一方。因此对于最初的一种分配方案,最终甲胜利等价于:“属于甲的先手胜利的袋子数量”严格大于“属于乙的先手胜利的袋子数量”。
记一个变量 \(x\) 表示“属于甲的先手胜利的袋子数量”减去“属于乙的先手胜利的袋子数量”,对每一个袋子决策它是分配给甲还是分配给乙。可以依据上面的数据求出,若分配给甲则对 \(x\) 有 \(+1 / 0\) 的贡献,若分配给乙则对 \(x\) 有 \(-1 / 0\) 的贡献。最终甲胜利等价于 \(x > 0\)。考虑贪心,先默认考虑贡献较少的那种选择,再将某些袋子调整至贡献较多的那种选择。记调整前的 \(x\) 值为 \(-\mathrm{sum}\),那么调整后需要至少增加 \(\mathrm{sum} + 1\) 的贡献甲才能胜利。
此时所有袋子可以划分成三类:调整后贡献增加 \(0 / 1 / 2\)。显然第一类袋子是无关紧要的,记这类袋子的数量为 \(\Delta\),那么再去掉这一类袋子后对后两类袋子计数,算出的答案再乘上 \(2^\Delta\) 即可。剩下的问题就是对后两类袋子进行计数,考虑双重循环,第一重枚举最终贡献的增加量,第二重枚举第三类袋子的数量,此时可以自然求出第一类袋子的数量,因此内部直接用组合数计数即可。这样我们就得到了一个 \(\Theta(n^2)\) 的算法。
代码($\Theta(n^2)$)
#include <cstdio>
#include <iostream>
using namespace std;
const int N=200003,mod=998244353;
int fact[N],finv[N];
int pow(int a,int b){int ans=1;while(b>0){if(b&1) ans=(long long)ans*a%mod;a=(long long)a*a%mod; b>>=1;}return ans;
}
void init_fact(int n){fact[0]=1;for(int i=1;i<=n;i++)fact[i]=(long long)fact[i-1]*i%mod;finv[n]=pow(fact[n],mod-2);for(int i=n-1;i>=0;i--)finv[i]=(long long)finv[i+1]*(i+1)%mod;
}
int C(int a,int b){if(a<0||b<0||b>a) return 0;return (long long)fact[a]*finv[b]%mod*finv[a-b]%mod;
}
int main(){
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);int i,j,x,y,s1,s2,n,cnt1=0,cnt2=0,lft=1,add=0,ans=0;scanf("%d%d%d",&n,&x,&y),init_fact(n);for(i=1;i<=n;i++){scanf("%d%d",&s1,&s2); bool s3,s4;if((x&1)==(y&1)) s3=s4=(x&1)?(s2&1):(s1+s2&1);else if(!s1) s3=s4=(s2&1);else if(s1>1) s3=!(x&1),s4=!(y&1);else s3=!(x&1)||(s2&1),s4=!(y&1)||(s2&1);add+=s4,(s3!=s4)?(cnt1++):(cnt2+=s3);if(!s3&&!s4) lft=lft*2%mod;}for(i=add+1;i<=cnt1+cnt2*2;i++){s1=max((i-cnt1+1)/2,0),s2=min(i/2,cnt2);for(j=s1;j<=s2;j++) ans=(ans+(long long)C(cnt2,j)*C(cnt1,i-j*2)%mod)%mod;}printf("%lld",(long long)ans*lft%mod);
// fclose(stdin);
// fclose(stdout);return 0;
}
考虑优化。观察上面的代码,我们发现第一个组合数只与 \(j\) 有关,第二个组合数同时与 \(i, j\) 有关,因此我们不妨交换循环顺序,将 \(j\) 提到最外层,将第一个组合数提到第一层循环进行计算。此时第二层循环内就只剩下了一个组合数,由于上标是个定值,因此我们只需进行前缀和优化即可。
时间复杂度为 \(\Theta(n)\)。
代码($\Theta(n)$)
#include <cstdio>
#include <iostream>
using namespace std;
const int N=200003,mod=998244353;
int fact[N],finv[N],sumC[N];
int pow(int a,int b){int ans=1;while(b>0){if(b&1) ans=(long long)ans*a%mod;a=(long long)a*a%mod; b>>=1;}return ans;
}
void init_fact(int n){fact[0]=1;for(int i=1;i<=n;i++)fact[i]=(long long)fact[i-1]*i%mod;finv[n]=pow(fact[n],mod-2);for(int i=n-1;i>=0;i--)finv[i]=(long long)finv[i+1]*(i+1)%mod;
}
int C(int a,int b){if(a<0||b<0||b>a) return 0;return (long long)fact[a]*finv[b]%mod*finv[a-b]%mod;
}
int main(){
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);int i,x,y,s1,s2,n,cnt1=0,cnt2=0,lft=1,add=0,ans=0;scanf("%d%d%d",&n,&x,&y),init_fact(n);for(i=1;i<=n;i++){scanf("%d%d",&s1,&s2); bool s3,s4;if((x&1)==(y&1)) s3=s4=(x&1)?(s2&1):(s1+s2&1);else if(!s1) s3=s4=(s2&1);else if(s1>1) s3=!(x&1),s4=!(y&1);else s3=!(x&1)||(s2&1),s4=!(y&1)||(s2&1);add+=s4,(s3!=s4)?(cnt1++):(cnt2+=s3);if(!s3&&!s4) lft=lft*2%mod;}for(i=1,sumC[0]=1;i<=cnt1;i++)sumC[i]=(sumC[i-1]+C(cnt1,i))%mod;for(i=0;i<=cnt2;i++){if(cnt1+i*2<=add) continue;s1=(sumC[cnt1]-((add<i*2)?0:sumC[add-i*2])+mod)%mod;ans=(ans+(long long)C(cnt2,i)*s1%mod)%mod;}printf("%lld",(long long)ans*lft%mod);
// fclose(stdin);
// fclose(stdout);return 0;
}