1. 程式人生 > >[HNOI&AHOI2017] NOIP考掛蒟蒻的一篇遊記 && 部分題解

[HNOI&AHOI2017] NOIP考掛蒟蒻的一篇遊記 && 部分題解

Day0

猶記得上次NOIP329卻切了Day1T2…這次似乎翻盤很困難……?

鹹魚選手試機寫了個Pollard-rho 震驚地發現srand(19260817)之後隨機的long long數竟然是2*大素數形式 還以為我寫錯了。

隔了一個座位的wyx敲了一個SA和FFT,期間穩爺爺約5-10分鐘來奶wyx霸霸一次,然後開始互奶。

後來wlp和zzy來了,場面一度失控(捂臉)

(可能是因為太久沒上語文了已經不會寫文章了)

Day1

拿到題看了看T1的spaly,畫了畫圖就扔了,感覺不是很可做。

看了看T2 似乎是道計數題 怕是還是可以做的 先扔了看T3去

然後看了下T3發現是傻逼題 直接敲了個NTT就過了樣例 拍了拍暴力資料 感覺沒啥錯 就去看前面的題了(全世界都A了T3)

於是又看了看T2 沒看出第一個情況只有O(n)種 感覺暴力還是很好寫的 先丟一邊了

研究T1的性質 發現單旋最小值就是隻有最小值的右子樹深度不變 其他都+1 單旋最大值也差不多 感覺還是不太可做

yy了一個splay維護整棵樹的dfs序的寫法 開始大力碼碼碼 碼好之後開始調調調 前後大概花了3h+ 總算是和暴力拍上了 感覺很興奮(然而只有本傻逼選手寫的是splay維護dfs序 wlp是LCT wyx直接一棵線段樹就艹過去了 為什麼不強制線上嗚嗚嗚 全世界都A了T1)

然後就玩了玩T2的暴力30分 寫了一個可持久化線段樹的60 不想拍了(其實來不及了有點慌)就直接交了

T2 第一個情況竟然那麼少?????????????

(全世界都A了T2)

出來沒有fst T1和T3我是很開心的 T2的暴力30也在預想之中 似乎把NOIP考掛的差距填回來了

Day2

這個T1資料範圍這麼小…全場都會做?

然後我就發現自己並不會什麼多項式做法 先去看T2

woc計算幾何?

滾去看T3

誒我好像不會 敲個70暴力吧。。

然後滾回去看T2 還是不會 跑去寫T1暴力

嗯這個T1暴力很靠譜……?於是碼了一個dp貪心了一下 然後dfs暴力一下 瞎jb剪剪枝

竟然A了?

然後又敲了個T2暴力 想了想加了一句

if (n >= 100000) {puts("nan");return 0;}

居然給我續了20pts……感覺會被ban掉……

於是day2就220……?懵--

wyx霸霸還是好穩啊 怒艹穩爺爺 可怕

wlp day2怎麼翻車了啊 不懂

-------------------------------------------分割線-------------------------------------------

放一下其中幾道題的題解吧。。

Day1T1 單旋 spaly

題意:給一棵spaly 要求支援插入 單旋最小值 單旋最大值 單旋刪除最小值 單旋刪除最大值 每次輸出上次操作節點深度 資料範圍1E5

聽說可以splay直接暴力維護這棵spaly 反正我是維護了整棵spaly的dfs序 然後我們來看每個操作

我們考慮 我們將每個點拆成了入(深度+1) 出(深度-1) 因此求一個點的深度其實就是求這個點的入/出在整個dfs序上的字首和,可以均攤O(log n)統計答案。

我們考慮dfs序怎麼維護,首先考慮操作1:加入一個節點 

顯然加入的這個節點要麼被掛在前繼的右邊 要麼被掛在後繼的左邊 而這取決於前繼和後繼誰的深度更小

因此我們只要查詢一下哪個是父親 如果是這個數的前繼 其右子樹一定為空 直接在它的出點前面插入即可

如果是這個數的後繼 左子樹一定為空 只要在入點後面插入

操作2:將最小值旋轉到根

我們發現最小值旋轉到根後 它只有右子樹 並且整個森林中其他值的dfs序位置不變

所以我們將這個最小值提出來 分開放到整個dfs序列的最前面和最後面就可以了

操作3:將最大值旋轉到根

和操作2相似

操作4和操作5:先做一次對應操作 再刪去兩個節點即可

時間複雜度O(nlogn)

#include"algorithm"
#include"iostream"
#include"stdlib.h"
#include"stdio.h"
#include"math.h"
#include"vector"
#include"queue"
#include"map"
#include"set"

using namespace std;

const int N=100005;

struct node{
	node*c[2],*fa;
	int flag,sum,sz;
	node();
	void pushup();
}Tnull,*null=&Tnull,a[2][N];

node::node(){
	c[0]=c[1]=fa=null;
	flag=0,sum=0,sz=0;
}

void node::pushup(){
	sz=c[0]->sz+c[1]->sz+1;
	sum=c[0]->sum+flag+c[1]->sum;
}

void rotate(node*x,node*&f){
	node*y=x->fa,*z=y->fa;
	if(y==f)f=x;else if(z!=null)z->c[z->c[1]==y]=x;
	int l=y->c[1]==x,r=l^1;
	if(x->c[r]!=null)x->c[r]->fa=y;
	y->fa=x,x->fa=z,y->c[l]=x->c[r],x->c[r]=y,y->pushup();
}

void splay(node*x,node*&f){
	while(x!=f){
		node*y=x->fa,*z=y->fa;
		if(y!=f){
			if((z->c[0]==y)^(y->c[0]==x))rotate(x,f);
			else rotate(y,f);
		}
		rotate(x,f);
	}
	x->pushup();
}

map <int,int> dat;
node*rt=null;

int query_deep(int x){
	node*y=&a[0][x];
	splay(y,rt);
	return y->c[0]->sum+y->flag;
}

void insert_back(node*x,node*i){
	splay(x,rt);
	i->c[1]=x->c[1];
	if(x->c[1]!=null)x->c[1]->fa=i;
	x->c[1]=i,i->fa=x;
	i->pushup(),x->pushup();
}

void insert_front(node*x,node*i){
	splay(x,rt);
	i->c[0]=x->c[0];
	if(x->c[0]!=null)x->c[0]->fa=i;
	x->c[0]=i,i->fa=x;
	i->pushup(),x->pushup();
}

int getsiz(int x){
	node*l=&a[0][x],*r=&a[1][x];
	splay(l,rt),splay(r,rt->c[1]);
	return rt->c[1]->c[0]->sz;
}

node*findfr(node*x){
	if(x->c[0]!=null)return findfr(x->c[0]);
	return x;
}

node*finded(node*x){
	if(x->c[1]!=null)return finded(x->c[1]);
	return x;
}

void insert(int x,int key){
	a[0][x].flag=1,a[0][x].pushup();
	a[1][x].flag=-1,a[1][x].pushup();
	if(rt==null){
		rt=&a[0][x],insert_back(rt,&a[1][x]);
		dat[key]=x;
		printf("%d\n",query_deep(x));
		return;
	}
	map<int,int>::iterator i=dat.lower_bound(key);
	if(i==dat.begin()){
		int d=i->second;
		insert_back(&a[0][d],&a[0][x]);
		insert_back(&a[0][x],&a[1][x]);
	}else if(i==dat.end()){
		--i;
		int d=i->second;
		insert_front(&a[1][d],&a[0][x]);
		insert_back(&a[0][x],&a[1][x]);
	}else{
		map<int,int>::iterator j=i;
		--i;
		if(getsiz(i->second)>getsiz(j->second)){
			int d=j->second;
			insert_back(&a[0][d],&a[0][x]);
			insert_back(&a[0][x],&a[1][x]);
		}else{
			int d=i->second;
			insert_front(&a[1][d],&a[0][x]);
			insert_back(&a[0][x],&a[1][x]);
		}
	}
	dat[key]=x;
	printf("%d\n",query_deep(x));
}

void erase(node*x){
	splay(x,rt);
	if(x->c[1]==null){
		rt=x->c[0];
		x->c[0]=null;
		return;
	}
	node*y=findfr(x->c[1]);
	splay(y,x->c[1]);
	rt=x->c[1];
	if(x->c[0]!=null)x->c[0]->fa=rt;
	rt->c[0]=x->c[0];
	x->c[1]=null,x->c[0]=null;
	rt->pushup();
}

void outtree(node*x){
	if(x->c[0]!=null)outtree(x->c[0]);
	if(x->flag==-1)printf("%d ",x-&a[1][0]);
	else printf("%d ",x-&a[0][0]);
	if(x->c[1]!=null)outtree(x->c[1]);
}

void makeroot(int id){
	int ans=query_deep(id);
	printf("%d\n",ans);
	erase(&a[0][id]);
	node*x=findfr(rt);
	insert_front(x,&a[0][id]);
	erase(&a[1][id]);
	node*y=finded(rt);
	insert_back(y,&a[1][id]);
}

void splay_mn(bool del){
	map<int,int>::iterator i=dat.begin();
	makeroot(i->second);
	if(del)dat.erase(i);
}

void splay_mx(bool del){
	map<int,int>::iterator i=dat.end();
	--i;
	makeroot(i->second);
	if(del)dat.erase(i);
}

void del_mn(){
	splay_mn(1);
	node*x=findfr(rt);
	node*y=finded(rt);
	erase(x),erase(y);
}

void del_mx(){
	splay_mx(1);
	node*x=findfr(rt);
	node*y=finded(rt);
	erase(x),erase(y);
}

int n,m;

int main(){
	freopen("splay.in","r",stdin);
	freopen("splay.out","w",stdout);
	null->c[0]=null->c[1]=null->fa=null;
	scanf("%d",&n);
	for(int i=1,opt,key;i<=n;i++){
		scanf("%d",&opt);
		switch(opt){
			case 1:scanf("%d",&key);insert(++m,key);break;
			case 2:splay_mn(0);break;
			case 3:splay_mx(0);break;
			case 4:del_mn();break;
			case 5:del_mx();break;
		}
	}
	return 0;
}

Day1T2 影魔 sf

題意 給定排列a 長度2E5 設L[i]為i左邊第一個大於a[i]的值 R[i]為i右邊第一個大於a[i]的值

2E5組l,r,求∑[l<=i<j<=r] [L[j]<=i && R[i]>=j] (p1-2p2) + ∑[l<=i<j<=r] [L[j]<=i || R[i]>=j] p2 



這是我的做法……可能比較繁瑣 但是起碼……是可以過的吧

時間複雜度O((6n+12m)logn)=O((n+m)logn)

#include"algorithm"
#include"iostream"
#include"stdlib.h"
#include"stdio.h"
#include"math.h"
#include"vector"
#include"queue"
#include"map"
#include"set"

#define last last_

using namespace std;
typedef long long LL;

const int N=200005;
int n,m,p1,p2,a[N],st[N],tp,L[N],R[N];
LL ans;

const int M=24000005;
LL sum[M];
int ls[M],rs[M],ndc;

void add(int&x,int last,int l,int r,int p,int u){
	x=++ndc,sum[x]=sum[last]+u,ls[x]=ls[last],rs[x]=rs[last];
	if(l==r)return;
	int mid=(l+r)>>1;
	if(p<=mid)add(ls[x],ls[last],l,mid,p,u);
	else add(rs[x],rs[last],mid+1,r,p,u);
}

LL qry(int x,int l,int r,int s,int t){
	if(sum[x]==0)return 0;
	if(l==s&&r==t)return sum[x];
	int mid=(l+r)>>1;
	if(t<=mid)return qry(ls[x],l,mid,s,t);
	if(s>mid)return qry(rs[x],mid+1,r,s,t);
	return qry(ls[x],l,mid,s,mid)+qry(rs[x],mid+1,r,mid+1,t);
}

LL ask(int*rt,int l,int r,int L,int R,int S,int T){
	return qry(rt[r],L,R,S,T)-qry(rt[l-1],L,R,S,T);
}

int rt[6][N];

int main(){
	freopen("sf.in","r",stdin);
	freopen("sf.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&p1,&p2);
	int w1=p1-2*p2,w2=p2;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		while(tp&&a[st[tp]]<a[i])st[tp--]=0;
		L[i]=st[tp];
		st[++tp]=i;
	}
	while(tp)st[tp--]=0;
	st[0]=n+1;
	for(int i=n;i>=1;i--){
		while(tp&&a[st[tp]]<a[i])st[tp--]=0;
		R[i]=st[tp];
		st[++tp]=i;
	}
	int d=n+1;
	for(int i=1;i<=n;i++){
		add(rt[0][i],rt[0][i-1],0,d,R[i],1);
		add(rt[1][i],rt[1][i-1],0,d,R[i],R[i]);
		add(rt[2][i],rt[2][i-1],0,d,L[i],1);
		add(rt[3][i],rt[3][i-1],0,d,L[i],L[i]);
		add(rt[4][i],rt[4][i-1],0,d,R[i],L[R[i]]<i);
		add(rt[5][i],rt[5][i-1],0,d,L[i],R[L[i]]>i);
	}
	for(int i=1,l,r;i<=m;i++,ans=0){
		scanf("%d%d",&l,&r);
		ans+=(ask(rt[1],l,r,0,d,0,r-1)+ask(rt[0],l,r,0,d,r,d)*r)*w2;
		ans-=(ask(rt[3],l,r,0,d,l+1,d)+ask(rt[2],l,r,0,d,0,l)*l)*w2;
		ans+=(ask(rt[4],l,r,0,d,0,r)+ask(rt[5],l,r,0,d,l,d))*w1;
		printf("%lld\n",ans);
	}
	return 0;
}


Day1T3 禮物 gift

題意 給兩個序列x和y 長度5E4 數字範圍1-100 求的最小值


然後就做完了 k可以暴力列舉 發現旋轉次數和k獨立 因為這個值很小 所以用NTT求一下那個和旋轉次數有關的值的最大值即可

時間複雜度O(nlogn+m)

#include"algorithm"
#include"iostream"
#include"stdlib.h"
#include"stdio.h"
#include"math.h"
#include"vector"
#include"queue"
#include"map"
#include"set"

using namespace std;

typedef long long LL;

const int N=50005,M=266666;
const int P=998244353,g=3;

int power(int a,int t,int P){
	int r=1;
	while(t){
		if(t&1)r=(LL)r*a%P;
		a=(LL)a*a%P;t>>=1;
	}
	return r;
}

int wn_[25];

void pre(){for(int i=0;i<22;i++)wn_[i]=power(g,(P-1)>>i,P);}

void transform(int A[],int len,int dft){
	int i,j=len>>1,k,l,c=0;
	for(i=1;i<len-1;i++){
		if(i<j)swap(A[i],A[j]);
		for(k=len>>1;j>=k;j-=k,k>>=1);
		j+=k;
	}
	for(l=2;l<=len;l<<=1){
		i=l>>1;
		int wn=wn_[++c];
		for(j=0;j<len;j+=l){
			int w=1;
			for(k=j;k<j+i;k++){
				LL u=A[k],v=(LL)A[k+i]*w%P;
				A[k]=(u+v)%P,A[k+i]=(u-v+P)%P;
				w=(LL)w*wn%P;
			}
		}
	}
	if(dft==-1){
		int inv_len=power(len,P-2,P);
		for(int i=0;i<len;i++)A[i]=(LL)A[i]*inv_len%P;
		for(int i=1;i<len/2;i++)swap(A[i],A[len-i]);
	}
}

int n,m,a[N],b[N],L,x[M],y[M],z[M],mx=0;

LL w0,w1,w2,ans;

LL calc(){
	ans=(LL)1e10;
	for(int k=-233;k<=233;k++)
		ans=min(ans,w0+w1*k+w2*k*k);
	return ans;
}

int main(){
	freopen("gift.in","r",stdin);
	freopen("gift.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)scanf("%d",&a[i]);
	for(int i=0;i<n;i++)scanf("%d",&b[i]);
	for(int i=0;i<n;i++)x[n-i-1]=a[i];
	for(int i=0;i<n;i++)y[i]=y[i+n]=b[i];
	for(L=1;L<=n*4;L<<=1);
	pre();
	transform(x,L,1);
	transform(y,L,1);
	for(int i=0;i<L;i++)z[i]=(LL)x[i]*y[i]%P;
	transform(z,L,-1);
	for(int i=n-1;i<=2*n-2;i++)mx=max(mx,z[i]);
	w0=-2*mx;
	for(int i=0;i<n;i++){
		w0+=a[i]*a[i];
		w0+=b[i]*b[i];
		w1+=2*b[i];
		w1-=2*a[i];
		w2++;
	}
	printf("%lld\n",calc());
	return 0;
}

Day2T1 大佬 dalao

題意略

我們發現懟dalao的次數和dalao的血量無關 所以我們可以先做一個dp 得到懟dalao的最多天數

然後我們就可以大力搜搜搜了

我們發現這個懟dalao的天數也很少 所以可以暴力列舉

首先考慮假如一次要懟一個dalao k點自信 的最少步數

我們可以列舉最高到達的等級 這個等級一定大於等於k的最大質因子 一定是k的約數 又不會超過步數上限

所以這裡可以剪很多 check一次雖然還是O(n^2) 但是會快很多

然後我們就可以暴力處理懟dalao一次的情況

對於懟兩次的情況 我們暴力步數較少的那一邊 就是我們搜尋這個懟掉的自信值k 然後大力剪剪枝就可以過了

我也不知道時間複雜度是什麼。

#include"algorithm"
#include"iostream"
#include"stdlib.h"
#include"string.h"
#include"stdio.h"
#include"math.h"
#include"vector"
#include"queue"
#include"map"
#include"set"

using namespace std;

const int jp[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
const int N=105,INF=(int)1e9;

int n,m,mc,a[N],w[N],C[N],dp[N][N],k,fl[N],maxc,tmp;

map<int,int> s;
int dat[1000005];

int find(int x){
	if(x<=1000000)return dat[x];
	return s[x];
}

void insert(int x,int ans){
	if(x<=1000000)dat[x]=ans;
	else s[x]=ans;
}

int calc(int n){
	if(n<0)return INF;
	if(n==0)return 0;
	if(n==1)return 1;
	int l=n,ed=1;
	for(int i=0;i<25;i++)while(l%jp[i]==0)ed=jp[i],l/=jp[i];
	if(l>1)return INF;
	if(find(n))return find(n);
	int lim=k+1;
	for(int i=ed;i<lim-1;i++)if(n%i==0){
		int t=i+1;
		l=n;
		for(int j=i;j>=2&&t<=lim&&l>1;j--)while(l%j==0){l/=j;++t;}
		if(l==1)lim=min(lim,t);
	}
	insert(n,lim);
	return lim;
}

void dfs(int x,int pos){
	if(tmp==m)return;
	int t=calc(x);
	if(t>k/2)return;
	for(int i=pos;i>=0;i--)if((long long)x*jp[i]<=maxc)dfs(x*jp[i],i);
	for(int i=0;i<=k-2*t;i++){
		for(int j=1;j<=m;j++)if(!fl[j]){
			int l=calc(C[j]-x-i);
			if(l+t+i<=k)fl[j]=1,++tmp;
		}
	}
}

int main(){
	freopen("dalao.in","r",stdin);
	freopen("dalao.out","w",stdout);
	scanf("%d%d%d",&n,&m,&mc);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)scanf("%d",&w[i]);
	for(int i=1;i<=m;i++)scanf("%d",&C[i]),maxc=max(maxc,C[i]);
	memset(dp,-1,sizeof(dp));
	dp[0][0]=mc;
	for(int i=0;i<n;i++){
		for(int j=0;j<=i;j++)if(dp[i][j]!=-1){
			if(dp[i][j]-a[i+1]<0)continue;
			dp[i+1][j]=min(mc,max(dp[i+1][j],dp[i][j]-a[i+1]+w[i+1]));
			dp[i+1][j+1]=min(mc,max(dp[i+1][j+1],dp[i][j]-a[i+1]));
		}
		for(int j=0;j<=n;j++)if(dp[i+1][j]!=-1)k=max(k,j);
	}
	for(int i=1;i<=m;i++){
		for(int j=0;j<k&&j<C[i];j++)fl[i]|=calc(C[i]-j)+j<=k;
		tmp+=fl[i];
	}
	dfs(1,14);
	for(int i=1;i<=m;i++)printf("%d\n",fl[i]);
	return 0;
}

Day2T2 隊長快跑 captain

計算幾何題 不會 會了再填坑

Day2T3 拋硬幣 coin

題意:問n次拋硬幣正面的數量比m次拋硬幣正面的數量多的方案數mod 10^k的值 n,m是1E18級別 k<=9 n>=m n-m<=10000

這個題好棒啊。。反正我是智商不太夠。。

首先我們考慮在n個裡面選i個 在m箇中選j個的方案數

就變成了 ∑[i>j]C(n,i)*C(m,j)

真是遺憾 反正我是不會繼續推式子了

但是我們可以將問題進行一次轉化

i>j可以轉化為i-j>0

你可能會問這有什麼重要的意義嗎 但是我們再將這個式子變一下 就變成了

i+m-j>m

問題就轉化為在n+m個物品裡面選擇超過m個物品的方案數 顯然我們把這些物品排好 每個左邊的n個和右邊的m個與上面的方案是一一對應的

所以式子就變成了∑[i=m+1...n+m] C(n+m,i)


但是還是有一個問題 這個組合數如何計算

我們很想暴力使用擴充套件Lucas定理 但是這樣就變成了O(Tk(n-m)logn)會TLE

可能會有一些卡常的技巧 但是這裡完全可以求出第一個組合數 mod 2^k 用 2^p*c 表示的結果 和 mod 5^k 用 5^p * c表示的結果

而我們做CRT時的exgcd值是固定的 可以一直使用

這樣的時間複雜度就是O(Tk(n-m+logn))的

其實這裡可以將k去掉 只要預處理出2和5的9次以下冪就可以了 但是似乎並沒有什麼必要

#include"bits/stdc++.h"

using namespace std;

typedef long long LL;

const int Mod=1000000000;
const int Mod1=1<<9,Mod2=Mod>>9;
const int ex1=1537323,ex2=-403;

void exgcd(int a,int b,int&x,int&y){
	if(b==0){x=1,y=0;return;}
	exgcd(b,a%b,y,x);y-=a/b*x;
}

int inv(int a,int mod){
	int x,y;exgcd(a,mod,x,y);
	return (x%mod+mod)%mod;
}

int CRT(int r2,int r5){return ((((LL)(r5-r2)*ex1%Mod<<9)+r2)%Mod+Mod)%Mod;}

int power(int a,LL t,int P){
	int r=1;
	while(t){
		if(t&1)r=(LL)r*a%P;
		a=(LL)a*a%P;t>>=1;
	}
	return r;
}

LL n,m;
int k;

char ot[23];
void output(int n){
	for(int i=1;i<=k;i++)ot[i]=n%10+'0',n/=10;
	for(int i=k;i>=1;i--)putchar(ot[i]);
	putchar('\n');
}

const int N=2000005;

int fac[2][N];

LL gd(LL n,int d){
	if(n<d)return 0;
	return gd(n/d,d)+n/d;
}

int getfac(LL n,int p,int mo){
	if(n<p)return fac[p==5][n];
	return (LL)fac[p==5][n%mo]*power(fac[p==5][mo-1],n/mo,mo)%mo*getfac(n/p,p,mo)%mo;
}

int F,G,FT,GT;

int C(LL n,LL m,bool fl=false){
	if(n<m)return 0;
	int ft=gd(n,2)-gd(m,2)-gd(n-m,2);
	int gt=gd(n,5)-gd(m,5)-gd(n-m,5);
	int fn=(LL)getfac(n,2,Mod1)*inv((LL)getfac(m,2,Mod1)*getfac(n-m,2,Mod1)%Mod1,Mod1)%Mod1;
	int gn=(LL)getfac(n,5,Mod2)*inv((LL)getfac(m,5,Mod2)*getfac(n-m,5,Mod2)%Mod2,Mod2)%Mod2;
	if(fl)F=fn,G=gn,FT=ft,GT=gt;
	fn=(LL)fn*power(2,ft,Mod1)%Mod1;
	gn=(LL)gn*power(5,gt,Mod2)%Mod2;
	return CRT(fn,gn);
}

int calc(LL n,LL m){
	int ans=power(2,n+m-1,Mod);
	if(m+1<=(n+m)/2){
		ans=(ans+C(n+m,m+1,1))%Mod;
		for(LL i=m+2,j,_F,_G;i<=(n+m)/2;i++){
			j=i;
			while(j%2==0)FT--,j/=2;
			F=(LL)F*inv(j%Mod1,Mod1)%Mod1;
			j=i;
			while(j%5==0)GT--,j/=5;
			G=(LL)G*inv(j%Mod2,Mod2)%Mod2;
			j=n+m-i+1;
			while(j%2==0)FT++,j/=2;
			F=(LL)F*(j%Mod1)%Mod1;
			j=n+m-i+1;
			while(j%5==0)GT++,j/=5;
			G=(LL)G*(j%Mod2)%Mod2;
			_F=(LL)F*power(2,FT,Mod1)%Mod1;
			_G=(LL)G*power(5,GT,Mod2)%Mod2;
			ans=(ans+CRT(_F,_G))%Mod;
		}
	}
	if(n+m&1^1)ans=(ans-C(n+m-1,(n+m)/2)+Mod)%Mod;
	return ans;
}

int main(){
	freopen("coin.in","r",stdin);
	freopen("coin.out","w",stdout);
	fac[0][0]=1;
	for(int i=1;i<=2*Mod1;i++){
		if(i%2==0)fac[0][i]=fac[0][i-1];
		else fac[0][i]=(LL)fac[0][i-1]*i%Mod1;
	}
	fac[1][0]=1;
	for(int i=1;i<=Mod2;i++){
		if(i%5==0)fac[1][i]=fac[1][i-1];
		else fac[1][i]=(LL)fac[1][i-1]*i%Mod2;
	}
	while(~scanf("%lld%lld%d",&n,&m,&k))output(calc(n,m));
	return 0;
}