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; }