Problem 720: Chess
Time Limit: 1000 ms Memory Limit: 255998 KB
Problem Description
在一個n*m的棋盤上,要求每一行每一列都至少有一個棋子的總方案數是多少?由於答案有可能很大,所以輸出答案對1000000007取模。
Input
兩個整數n和m。
30% 1<=n,m<=5
100% 1<=n,m<=50
Output
一個整數表示總方案數模1000000007的值。
Sample Input
2 3
Sample Output
25
本題各種做法
dp(n^3)
dp[ i ] [ j ] 表示前i行中有j列放有棋子的方案數
#include<cstdio>
#include<iostream>
#define MOD 1000000007
#define N 60
using namespace std;
typedef long long LL;
LL a[N],dp[N][N],p,c[N][N];
int n,m;
int main(){
scanf("%d%d",&n,&m);
c[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<=i;j++)
if(j==0)c[i][j]=1;
else c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
a[0]=1;
for(int i=1;i<=50;i++)a[i]=a[i-1]*2%MOD;
dp[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<j;k++){
p=(dp[i-1][k]*a[k]%MOD)*c[m-k][j-k]%MOD;
dp[i][j]=(dp[i][j]+p)%MOD;
}
p=dp[i-1][j]*(a[j]-1)%MOD;
dp[i][j]=(dp[i][j]+p)%MOD;
}
}
cout<<dp[n][m];
}
lzk(n^4)
#include<cstdio>
#define ll long long
using namespace std;
const int N=50+5;
const ll mod=1e9+7;
ll c[N][N];
ll f[N][N];
ll qpow(int x) {
if(x==0) return 1ll;
ll ans=qpow(x/2);
ans=ans*ans%mod;
if(x&1) ans=ans*2%mod;
return ans%mod;
}
int main() {
c[0][0]=1;
for(int i=1; i<=50; i++) {
c[i][0]=1;
for(int j=1; j<=i; j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
int n, m;
scanf("%d%d", &n, &m);
f[1][1]=1;
for(int x=1; x<=n; x++)
for(int y=1; y<=m; y++) {
if(x==1&&y==1) continue;
f[x][y]=qpow(x*y);
for(int i=0; i<x; i++)
for(int j=0; j<y; j++) {
if(i==0&&j==0) continue;
f[x][y]=(f[x][y]+mod-c[x][i]*c[y][j]%mod*f[x-i][y-j]%mod)%mod;
}
f[x][y]=(f[x][y]-1)%mod;
}
printf("%lld\n", f[n][m]);
/*
printf("\n");
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) printf("%lld ", f[i][j]);
printf("\n");
}
*/
return 0;
}
hz 容斥原理
最後是加或減要看n的奇偶
#define mod 1000000007
using namespace std;
typedef long long ll;
int m,n,i;
ll ans;
ll pow(ll t,int k) {
ll res = 1;
for (int i=0;(k>>i);i++) {
if ((k>>i)&1) res = (res * t) % mod;
t = (t * t) % mod;
} return res;
}
ll C(int a,int b) {
ll res = 1;
if (b*2 > a) b = a - b;
for (int i=0;i<b;i++) res = ((res * (a-i)) % mod) * pow(i+1,mod-2) % mod;
return res;
}
int main() {
cin >> n >> m;
for (i=n;i;i--) {
if ((n-i) & 1) ans = (ans + mod - (C(n,n-i) * pow(pow(2,i)-1,m) % mod)) % mod;
else ans = (ans + C(n,n-i) * pow(pow(2,i)-1,m)) % mod;
}
cout << ans;
}
ssn&lsb 遞歸
#include<iostream>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=60, MOD=1000000007;
ll f[maxn], ys[maxn], n, m;
ll mi(ll a, ll k) {
if(k == 0) return 1;
if(k == 1) return a%MOD;
ll l = mi(a, k/2)%MOD;
if(k & 1) return ((l*l%MOD)*a)%MOD;
else return (l*l)%MOD;
}
ll C(ll a, ll k) {
memset(ys, 0, sizeof(ys));
ll ret = 1;
for(int i = a; i >= a-k+1; i--) {
int b = i;
for(int j = 2; j <= b; j++) while(b%j == 0)
{ys[j]++; b /= j; }
}
for(int i = k; i >= 1; i--) {
int b = i;
for(int j = 2; j <= b; j++) while(b%j == 0)
{ys[j]--; b /= j; }
}
for(int i = 1; i <= 50; i++) while(ys[i]--)
ret = (ret*i)%MOD;
return ret%MOD;
}
ll solve(ll x) {
if(f[x]) return f[x];
ll ret;
ret = mi(mi((ll)2, x)-1, m);
for(int i = 1; i <= x; i++)
ret = (ret-C(x, i) * solve(x-(ll)i)+MOD)%MOD;
f[x] = ret;
return ret%MOD;
}
int main() {
cin >> n >> m;
cout << solve(n) << endl;
return 0;
}
Problem 721: INTSUB
Time Limit: 1000 ms Memory Limit: 255999 KB
Problem Description
給出一個大小爲2n的集合X={1,2,3,…,2n-1,2n},問該集合中有趣子集的數目,答案mod1e9+7。
x的有趣子集定義爲,該子集中至少有兩個數,a和b,b是a的倍數且a是集合中最小的元素。
Input
輸入一個整數n
40% 1<=n<=50
100% 1<=n<=1000
Output
輸出子集數目mod1e9+7的結果
Sample Input
3
Sample Output
47
分析
b是a的倍數
b有【n/a】個
所以答案爲
完整AC代碼
其實不用快速冪
#include<cstdio>
#include<iostream>
#define MOD 1000000007
#define N 1010
using namespace std;
typedef long long LL;
int n,p;
LL a[2*N],ans,q;
LL Qpow(int q){
LL p=2,ans=1;
while(q!=0){
if(q%2)ans=(ans*p)%MOD;
p=(p*p)%MOD;
q/=2;
}
return ans;
}
int main(){
scanf("%d",&n);
n*=2;
// for(int i=0;i<=n;i++)a[i]=Qpow(i);
// for(int i=0;i<=n;i++)cout<<a[i]<<'\n';
for(int i=1;i<=n/2;i++){
p=(n/i)-1;
if(!a[p])a[p]=Qpow(p);
if(!a[n-i-p])a[n-i-p]=Qpow(n-i-p);
q=((a[p]-1)*a[n-i-p])%MOD;
ans=(ans+q)%MOD;
// cout<<ans<<'\n';
}
cout<<ans;
}
Problem 722: MINSUB
Time Limit: 3000 ms Memory Limit: 256000 KB
Problem Description
給一個n*m矩陣M,和一個整數k,對於矩陣M的子矩陣M’,定義min(M’)爲M’矩陣中元素的最小值。
我們需要找出這樣一個子矩陣,該矩陣的面積至少爲K,且min(M’)最大化。在min(M’)相同的情況下,面積越大越好。面積的定義爲該矩陣的行數*列數。
Input
第一行兩個整數n和m,表示矩陣的大小。
30% 1<=n,m<=50
100% 1<=n,m<=1000 1<=k<=n*m
Output
輸出兩個值,第一個是最大的min(M’),第二個是該矩陣的最大面積。
Sample Input
3 3 2
1 2 3
4 5 6
7 8 9
Sample Output
8 2
分析
二分矩陣元素大小最小值
把矩陣轉換爲0-1矩陣
掃描線用單調隊列(棧)統計比當前小的元素的位置
反過來再掃一邊
統計答案
繼續二分
時限3s
完整AC程序(3s,stl棧)
#include<cstdio>
#include<iostream>
#include<stack>
#define N 1010
using namespace std;
struct Node{
int v,w;
};
stack<Node>s;
int n,m,a[N][N],f[N][N],b[N],l,r,mid,c[N],k,ans,num,w;
int Solve(int x){
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]>=x)f[i][j]=f[i-1][j]+1;
else f[i][j]=0;
for(int i=1;i<=n;i++){
while(!s.empty()) s.pop();
for(int j=1;j<=m;j++){
while(!s.empty()&&s.top().v>f[i][j]) s.pop();
if(!s.empty()){
if(s.top().v==f[i][j])b[j]=b[s.top().w];
else b[j]=s.top().w+1;
}
else b[j]=j;
s.push((Node){f[i][j],j});
}
while(!s.empty()) s.pop();
for(int j=m;j>=1;j--){
while(!s.empty()&&s.top().v>f[i][j]) s.pop();
if(!s.empty()){
if(s.top().v==f[i][j])c[j]=b[s.top().w];
else c[j]=s.top().w-1;
}
else c[j]=j;
s.push((Node){f[i][j],j});
}
for(int j=1;j<=m;j++)
ans=max(ans,(c[j]-b[j]+1)*f[i][j]);
}
return ans;
}
int main(){
freopen("data.txt","r",stdin);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
r=max(r,a[i][j]);
}
l=0;
while(l<r){
int mid=(l+r)/2;
if((w=Solve(mid))>=k){
if(mid>ans){
ans=mid;
num=w;
}
l=mid+1;
}
else r=mid;
}
if((w=Solve(l))>=k){
if(l>ans){
ans=l;
num=w;
}
}
printf("%d %d\n",ans,num);
}
標程(二分經我優化,可以跑進2s,類似kmp自配函數的單調隊列)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, a[1001][1001], test, k, f[1001][1001], U[1001], D[1001],ma;
int calc(int now) {
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (a[i][j] >= now) f[i][j] = f[i][j - 1] + 1;
else f[i][j] = 0;
int area = 0;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
int now = j - 1;
for (; now && f[j][i] <= f[now][i]; now = U[now]);
U[j] = now;
}
for (int j = n; j; --j)
{
int now = j + 1;
for (; now != n + 1 && f[j][i] <= f[now][i]; now = D[now]);
D[j] = now;
}
for (int j = 1; j <= n; j++)
area = max(area, (D[j] - U[j] - 1) * f[j][i]);
}
return area;
}
int main() {
freopen("data.txt","r",stdin);
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++){
scanf("%d", &a[i][j]);
ma=max(ma,a[i][j]);
}
int Left = 0, Right = ma, Mid = (Left + Right) >> 1;
for (; Left + 1 < Right; Mid = (Left + Right) >> 1)
if (calc(Mid) >= k)
Left = Mid;
else
Right = Mid;
printf("%d %d\n", Left, calc(Left));
}