水题,补成一个大三角形,然后减去缺的三个小三角形。
#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;
}
判断两个串“等价”。等价的定义是,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;
}
一个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;
}
本文深入探讨了编程中的几种核心算法和问题解决策略,包括但不限于三角形计算、字符串等价判断、棋盘路径计数等。通过详细解释每种方法的原理和实现过程,旨在帮助读者提升算法理解和编程能力。

被折叠的 条评论
为什么被折叠?



