【bzoj4806~bzoj4808】炮车马后——象棋四连击
bzoj4806——炮
题目传送门:bzoj4806
这种题一看就是dp。。。我们可以设$ f[i][j][k] $表示处理到第$ i $行,有$ j $列没放炮,$ k $列只放了一个炮。接着分情况讨论:第$ i $行不放炮、放一个炮、放两个炮;放在只有一个炮的列上,还是放在没炮的列上。于是就可以快乐地列方程了:
$ \begin{equation} \begin{split} f[i][j][k] &= f[i-1][j][k] \\ &+ (j+1)\cdot f[i-1][j+1][k-1] \\ &+ (k+1)\cdot f[i-1][j][k+1] \\ &+ \binom{j+2}{2} \cdot f[i-1][j+2][k-2] \\ &+ (j+1)\cdot k\cdot f[i-1][j+1][k] \\ &+ \binom{k+2}{2}\cdot f[i-1][j][k+2] \end{split} \end{equation} $
另外,此题有双倍经验:bzoj1801(我是不会告诉你模数不一样的)
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<ctime> #include<algorithm> #include<queue> #include<vector> #define ll long long #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define inf 0x7fffffff #define mod 999983 #define eps 1e-20 ll read() { ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=tmp*10+c-'0'; return tmp*f; } using namespace std; ll f[110][110][110]; int n,m; int main() { int i,j,k; n=read(); m=read(); f[0][m][0]=1; for(i=1;i<=n;i++) for(j=0;j<=m;j++) for(k=0;k+j<=m;k++){ f[i][j][k]=f[i-1][j][k]+f[i-1][j][k+1]*(k+1)+f[i-1][j][k+2]*(k+2)*(k+1)/2+f[i-1][j+1][k]*k*(j+1); if(k)f[i][j][k]+=f[i-1][j+1][k-1]*(j+1); if(k>1)f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2; f[i][j][k]%=mod; } ll ans=0; for(j=0;j<=m;j++) for(k=0;k+j<=m;k++) ans=(ans+f[n][j][k])%mod; printf("%lld\n",ans); }
bzoj4807——车
题目传送门:bzoj4807
这题一看就是组合数。。。就是加了个高精度罢了。
代码:
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<ctime> #include<algorithm> #include<queue> #include<vector> #include<map> #define ll long long #define ull unsigned long long #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define lowbit(x) (x& -x) #define mod 1000000007 #define inf 0x3f3f3f3f #define eps 1e-18 #define maxn 1000010 inline ll read(){ll tmp=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-'0'; return tmp*f;} inline ll power(ll a,ll b){ll ans=1; for(;b;b>>=1){if(b&1)ans=ans*a%mod; a=a*a%mod;} return ans;} inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline void swap(int &a,int &b){int tmp=a; a=b; b=tmp;} using namespace std; struct hp{ char num[60]; friend hp operator * (hp a,int b){ int tmp=0; hp c; memset(&c,0,sizeof(c)); for(int i=0;i<50;i++){ tmp=tmp+b*a.num[i]; c.num[i]=tmp%10; tmp/=10; } return c; } void print(hp a){ int k=49; while(k&&!a.num[k])--k; for(int i=k;i>=0;i--) printf("%d",a.num[i]); printf("\n"); } }; ll p[maxn],mn[maxn],tot[maxn]; int n,m,cnt=0; void eular(int n) { for(int i=2;i<=n;i++){ if(!mn[i])mn[i]=++cnt,p[cnt]=i; for(int j=1;j<=mn[i]&&1ll*i*p[j]<=n;j++) mn[i*p[j]]=j; } } int main() { n=read(); m=read(); if(n<m)swap(n,m); eular(n); for(int i=1;i<=cnt;i++) for(ll j=p[i];j<=n;j*=p[i]) tot[i]+=n/j; for(int i=1;i<=cnt;i++) for(ll j=p[i];j<=m;j*=p[i]) tot[i]-=m/j; for(int i=1;i<=cnt;i++) for(ll j=p[i];j<=n-m;j*=p[i]) tot[i]-=(n-m)/j; hp ans; memset(&ans,0,sizeof(ans)); ans.num[0]=1; for(int i=1;i<=cnt;i++) for(int j=1;j<=tot[i];j++) ans=ans*p[i]; ans.print(ans); }
bzoj4808——马
题目传送门:bzoj4808
其实如果把棋盘相邻块黑白染色,那么我们可以发现马每跳一步只会跳到异色格,所以直接互相连边,跑二分图匹配就行了。
听说您像我一样用dinic跑二分图匹配?那得加当前弧优化。(至少我要加才能过)
另外,此题也有双倍经验:bzoj3175
代码:
#include<cstdio> #include<cstring> #define min(a,b) (a<b?a:b) #define inf 0x3f3f3f3f using namespace std; const int dx[8]={1,-1,2,-2,2,-2,1,-1},dy[8]={2,2,1,1,-1,-1,-2,-2}; struct edge{ int to,nxt,flow; }e[800010]; int fir[40010],lv[40010],q[40010],cur[40010]; int mp[210][210]; int n,m,S,T,tot=0; void add(int x,int y,int flow) { e[tot].to=y; e[tot].flow=flow; e[tot].nxt=fir[x]; fir[x]=tot++; e[tot].to=x; e[tot].flow=0; e[tot].nxt=fir[y]; fir[y]=tot++; } int dfs(int now,int flow) { if(now==T)return flow; int tot=0; for(int i=cur[now];~i;i=e[i].nxt) if(e[i].flow&&lv[e[i].to]==lv[now]+1){ cur[now]=i; int tmp=dfs(e[i].to,min(flow,e[i].flow)); e[i].flow-=tmp; e[i^1].flow+=tmp; tot+=tmp; flow-=tmp; if(!flow)break; } return tot; } int dinic() { int i,ans=0; while(1){ for(i=0;i<=T;i++)lv[i]=0,cur[i]=fir[i]; int h=1,t=1; q[1]=S; lv[S]=1; while(h<=t){ for(i=fir[q[h]];~i;i=e[i].nxt) if(e[i].flow&&!lv[e[i].to]){ q[++t]=e[i].to; lv[e[i].to]=lv[q[h]]+1; } ++h; } if(!lv[T])return ans; int k=dfs(S,inf); while(k)ans+=k,k=dfs(S,inf); } } int main() { memset(fir,255,sizeof(fir)); scanf("%d%d",&n,&m); S=0; T=n*m+1; int cnt=0; for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&mp[i][j]),cnt+=!mp[i][j]; for(int i=0;i<n;i++) for(int j=0;j<m;j++) if((i^j)&1){ for(int k=0;k<8;k++){ int xx=i+dx[k],yy=j+dy[k]; if(xx<0||xx>=n||yy<0||yy>=m)continue; add(i*m+j+1,xx*m+yy+1,1); } } for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(!mp[i][j]){ if((i^j)&1)add(S,i*m+j+1,1); else add(i*m+j+1,T,1); } printf("%d\n",cnt-dinic()); }
bzoj4809——皇后
题目传送门:bzoj4810
其实数据水,爆搜就能过,而且这道题好像也没什么靠谱的解法。
代码:
#include<cstdio> int mp[20][20],vis0[20],vis1[40],vis2[40]; int n,ans=0; void dfs(int now) { if(now>n){ ++ans; return; } for(int i=1;i<=n;i++) if(!mp[now][i]&&!vis0[i]&&!vis1[now+i]&&!vis2[n+now-i]){ vis0[i]=1; vis1[now+i]=1; vis2[n+now-i]=1; dfs(now+1); vis0[i]=0; vis1[now+i]=0; vis2[n+now-i]=0; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&mp[i][j]); dfs(1); printf("%d\n",ans); }