Codeforces Round #313 (Div. 1) A B C

A Gerald's Hexagon

        水题,补成一个大三角形,然后减去缺的三个小三角形。

#include <bits/stdc++.h>    
  
using namespace std;    

#define ll long long
const int maxn=100010;

int a[10];
int tb[3010];

int main(){
	int delta=1;
	for(int i=1;i<=3000;i++){
		tb[i]=tb[i-1]+delta;
		delta+=2;
	}
	
	for(int i=1;i<=6;i++){
		cin>>a[i];
	}
	int len=a[1]+a[2]+a[3];
	int ans=tb[len];
	ans-=tb[a[1]];
	ans-=tb[a[3]];
	ans-=tb[a[5]];
	cout<<ans<<endl;
	return 0;
}

 

B Equivalent Strings

        判断两个串“等价”。等价的定义是,1.相等;2.当串长度为偶数时,把两个串分别分为左右两半,串a的左半部分和串b的右半部分等价,串a的右半部分和串b的左半部分等价。注意这是一个递归的定义。

        普通的dfs分治一下就可以过。有个效率更高的做法,就是对两个串分别单独处理,归并排序使得字典序最小,然后判两个串是否相等。

#include <bits/stdc++.h>    
  
using namespace std;    

#define ll long long
const int maxn=100010;

char a[200010];
char b[200010];

bool dp[21][200010];
int len;
int h;
static int cnt=0;

bool dfs(int lv,int ida,int idb){
	cnt++;
	if(lv==h){
		int curlen=len>>lv;
		int posa=curlen*ida-curlen;
		int posb=curlen*idb-curlen;
		for(int i=0;i<curlen;i++){
			if(a[posa]!=b[posb])return 0;
			posa++;	posb++;
		}
		return 1;
	}
	bool re=0;
	if(dfs(lv+1,ida*2-1,idb*2-1)&&dfs(lv+1,ida*2,idb*2) ){
		return 1;
	}
	if(dfs(lv+1,ida*2-1,idb*2)&&dfs(lv+1,ida*2,idb*2-1) ){
		return 1;
	}
	return 0;
}

int main(){
	scanf("%s%s",a,b);
	len=strlen(a);
	int tmp=len;
	h=0;
	while(tmp%2==0){
		h++;
		tmp>>=1;
	}
	bool ok = dfs(0,1,1);
	if(ok){
		cout<<"YES"<<endl;
	}else{
		cout<<"NO"<<endl;
	}
	
	return 0;
}

C Gerald and Giant Chess

        一个h*w的网格,你需要从(1,1)走到(h,w),只能朝着h或w的方向走。另外,有n个坏的格子是不能走的,问有多少条路径。

        首先很容易想到的是,如果没有坏格子,从u(x1,y1)走到v(x2,y2)的路径数是C(x2-x1+y2-y1,x2-x1),记为calc(u,v)。然后这道题可以用dp来做,dp(s,i)表示从起点走到第i个坏点有多少种路径。如果可以这样走:s->u->v,那么dp(s,v)=calc(s,v)-dp(s,u)*calc(u,v),注意不是dp(s,v)=calc(s,v)-dp(s,u)*dp(u,v),我比赛时就一直认为是这样所以没做出来。另外组合数取模需要逆元,可以用费马小定理计算。通过这题,我学习到了费马小定理。

#include <bits/stdc++.h>    
  
using namespace std;    
#define ll long long

const int mod = 1e9+7;
int h,w,n;

ll Pow(ll a,int n){
	ll re=1;
	while(n){
		if(n&1){
			re*=a;
			re%=mod;
		}
		a*=a;
		a%=mod;
		n>>=1;
	}
	return re;
}

struct Point{
	int r,c;
	bool operator<(const Point& other)const{
		if(r==other.r)return c<other.c;
		return r<other.r;
	}
}pt[2010];

ll dp[2010];

int inv[200010];	//i的阶乘的逆元 

ll fab[200010];  
  
//组合数   
ll C(int n,int k){  
    ll res=fab[n]*inv[k];  
    res%=mod;  
    res*=inv[n-k];  
    res%=mod;  
    return res;  
}  

int main(){
	fab[0]=1;  
    for(int i=1;i<=200000;i++){  
        fab[i]=fab[i-1]*i;  
        fab[i]%=mod;  
    }
    
    inv[0]=1;	//注意这个不能漏 
    for(int i=1;i<=200000;i++){	//费马小定理计算阶乘的逆元 
		inv[i]=Pow(fab[i],mod-2);
	}
	cin>>h>>w>>n;
	
	for(int i=1;i<=n;i++){
		scanf("%d%d",&pt[i].r,&pt[i].c);
	}
	sort(pt+1,pt+n+1);	//排序后,就可以递推dp,不用记忆化搜索 
	pt[0].r=pt[0].c=1;
	pt[n+1].r=h;	pt[n+1].c=w;

	for(int v=1;v<=n+1;v++){
		dp[v]=C(pt[v].r-pt[0].r+pt[v].c-pt[0].c,pt[v].r-pt[0].r);
		for(int i=1;i<v;i++){
			if(pt[i].c<=pt[v].c){
				ll tmp=dp[i]*C(pt[v].r-pt[i].r+pt[v].c-pt[i].c,pt[v].r-pt[i].r);
				tmp%=mod;
				dp[v]-=tmp;
				dp[v]+=mod;
				dp[v]%=mod;
			}
		}
	}
	
	cout<<dp[n+1]<<endl;
	return 0;
}

你可能感兴趣的:(codeforces)