7_11模擬賽

gfoj

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 容斥原理

i=0n(1)i×Cin×(2ni1)m

(2n1)mC(n,1)(2n11)m+C(n,2)(2n21)m....+...

最後是加或減要看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】個
所以答案爲

i=1n2n/i12nin/i+1

完整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));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章