1. 程式人生 > >HDU 6172 and HDU 6185 【線性遞推 + 思維 + 板子】

HDU 6172 and HDU 6185 【線性遞推 + 思維 + 板子】

這兩道題都是給的線性遞推式(輸入只有一個未知數n), 那麼我寫這個部落格的目的就是儲存一個超強模板, 可以解決任何線性遞推式. 這個板子是我從百度之星複賽上”偷”的杜教的板子. 所以我們現在要做的是用絕對正確的方法求出遞推式的前幾項. 然後扔進這個板子就可以了.

//這些板子都可以直接用, 不用管mod, 帶了mod的. 所以用的時候只用改mod的值和前幾項的數值. 當然前幾項丟的越多越好, 一般8個是絕對可以推出來的. 個別的少一點也行.
HDU 6172
//題意不多說.
AC Code

#include <cstdio>
#include <cstring>
#include <cmath> #include <algorithm> #include <vector> #include <string> #include <map> #include <set> #include <cassert> using namespace std; typedef long long ll; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back
#define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second #define SZ(x) ((ll)(x).size()) typedef vector<ll> VI; typedef pair<ll,ll> PII; const ll mod = 1000000007; ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return
res;} // head ll _,n; namespace linear_seq { const int N=10010; ll res[N],base[N],_c[N],_md[N]; vector<ll> Md; void mul(ll *a,ll *b,ll k) { rep(i,0,k+k) _c[i]=0; rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod; for (ll i=k+k-1;i>=k;i--) if (_c[i]) rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod; rep(i,0,k) a[i]=_c[i]; } ll solve(ll n,VI a,VI b) { // a 係數 b 初值 b[n+1]=a[0]*b[n]+... // printf("%d\n",SZ(b)); ll ans=0,pnt=0; ll k=SZ(a); assert(SZ(a)==SZ(b)); rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1; Md.clear(); rep(i,0,k) if (_md[i]!=0) Md.push_back(i); rep(i,0,k) res[i]=base[i]=0; res[0]=1; while ((1ll<<pnt)<=n) pnt++; for (ll p=pnt;p>=0;p--) { mul(res,res,k); if ((n>>p)&1) { for (ll i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0; rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod; } } rep(i,0,k) ans=(ans+res[i]*b[i])%mod; if (ans<0) ans+=mod; return ans; } VI BM(VI s) { VI C(1,1),B(1,1); ll L=0,m=1,b=1; rep(n,0,SZ(s)) { ll d=0; rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod; if (d==0) ++m; else if (2*L<=n) { VI T=C; ll c=mod-d*powmod(b,mod-2)%mod; while (SZ(C)<SZ(B)+m) C.pb(0); rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; L=n+1-L; B=T; b=d; m=1; } else { ll c=mod-d*powmod(b,mod-2)%mod; while (SZ(C)<SZ(B)+m) C.pb(0); rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; ++m; } } return C; } ll gao(VI a,ll n) { VI c=BM(a); c.erase(c.begin()); rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod; return solve(n,c,VI(a.begin(),a.begin()+SZ(c))); } }; int main() { for (scanf("%lld",&_);_;_--){ scanf("%lld",&n); printf("%lld\n",linear_seq::gao(VI{31,197,1255,7997},n-2)); } }

HDU 6185
//題意不多說.
AC Code

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <cassert>
using namespace std;
typedef long long ll;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((ll)(x).size())
typedef vector<ll> VI;
typedef pair<ll,ll> PII;
const ll mod = 1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
// head

ll _,n;
namespace linear_seq {
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];

    vector<ll> Md;
    void mul(ll *a,ll *b,ll k) {
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
        for (ll i=k+k-1;i>=k;i--) if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    ll solve(ll n,VI a,VI b) {
        // a 係數 b 初值 b[n+1]=a[0]*b[n]+...
//        printf("%d\n",SZ(b));
        ll ans=0,pnt=0;
        ll k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (ll p=pnt;p>=0;p--) {
            mul(res,res,k);
            if ((n>>p)&1) {
                for (ll i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        ll L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    ll gao(VI a,ll n) {
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};

int main() {
    while(~scanf("%lld",&n)){
        printf("%lld\n",linear_seq::gao(VI{1,5,11,36,95,281,781,2245},n-1));
    }
}

//對比一下這兩個板子也可以知道就是改的那部分. 還有注意的是具體傳多少項, 和後面那個傳n的什麼樣子, 都是試出來的, 要自己感覺對了, 即把後面的幾項打出來看. 那麼才有可能對. 所以還是要試出來的.
//需要宣告的一點是這個序列前幾項怎麼推出. 第一個例題前面幾項還是比較好推的, 根據題目所給的式子.
那麼後面一個例題, 前面幾項怎麼推的還是有一定難度的(其實知道前面幾項了, 也是可以找到遞推式子的, 當然用矩陣快速冪做也是可以的, 但是明顯這個板子是很強的呀(矩陣快速冪寫起來還是非常複雜的)!!! 但注意這個板子只能適用於任何的線性遞推式子, 其他的遞推式不一定適用), 所以正解就是用二進位制列舉或者是dfs深搜出前面幾項. 然後套一套杜教的板子就可以了. (或者是自己根據這幾項找遞推式子)

附上打表找前幾項的程式碼: 只要能準確的找出來, 再暴力的方法都是允許的.
dfs版本: (也是比較容易想到的, 注意細節處理就行)

/** @Cain*/
int n,cnt;
int mapp[7][15];

bool ok(int &x,int &y)
{
    for(int i=1;i<=4;i++){
        for(int j=1;j<=n;j++){
            if(!mapp[i][j]){
                x = i;
                y = j;
                return false;
            }
        }
    }
    return true;
}

void dfs(int x,int y)
{
    if(!mapp[x+1][y] && x+1<=4){
        mapp[x][y] = mapp[x+1][y] = 1;   // 縱向的填.
        int tmpx,tmpy;
        if(ok(tmpx,tmpy)){
            mapp[x][y] = mapp[x+1][y] = 0; //注意填滿的時候的處理.
            cnt++;
            return ;
        }
        dfs(tmpx,tmpy);   // 從沒有填滿的地方繼續搜尋.
        mapp[x][y] = mapp[x+1][y] = 0;  //回溯的時候注意處理.
    }
    if(!mapp[x][y+1] && y+1<=n){  
        mapp[x][y] = mapp[x][y+1] = 1;   // 橫向的填.模仿著上面的來做.
        int tmpx,tmpy;
        if(ok(tmpx,tmpy)){
            mapp[x][y] = mapp[x][y+1] = 0;
            cnt++;
            return ;
        }
        dfs(tmpx,tmpy);
        mapp[x][y] = mapp[x][y+1] = 0;  
    }
}

int main()
{
    for(int i=1;i<=10;i++){
        n = i;
        cnt = 0;
        dfs(1,1);
        printf("%d%c",cnt,i==10?'\n':',');
    }
} //然後直接丟進杜教的板子就可以了.xx

二進位制列舉版本: (這個想法是非常重要的, 很多題都可以用這樣打出表來) 跑前幾項還是要跑一會的. 但跑出來了就是贏了. 這個版本相對於dfs的肯定是要慢很多的. 但是在有些情況下是非常有用的.

/** @Cain*/
typedef long long int ll ;
int mapp[10][20];
bool ok(int n, int m) {
    int vis[10][20] = {0};   //表示當前這個格子是否用過. 保證只用一次.
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (vis[i][j] == 0) {
                int f = 0;
                if (mapp[i][j] == 0) {
                    int tx = i;
                    int ty = j + 1;
                    if (ty <= m && vis[tx][ty] == 0 && mapp[tx][ty] == mapp[i][j]) {
                        f = 1;
                        vis[tx][ty] = 1;
                        vis[i][j] = 1;
                    }
                }
                else {
                    int tx = i + 1;
                    int ty = j;
                    if (tx <= n && vis[tx][ty] == 0 && mapp[tx][ty] == mapp[i][j]) {
                        f = 1;
                        vis[tx][ty] = 1;
                        vis[i][j] = 1;
                    }
                }
                if (!f) return false;
            }
        }
    }
    return true;
}

void solve() {
    for (int i = 1; i <= 10; i++) {   //i 是列數.
        int tot = i * 4;
        ll num = 0;
        for (ll st = 0; st < (1ll<<tot); st++) {   //將這些所有的格子看成二進位制的一位.
            ll cur = st;                       //通過二進位制列舉就可以一個不漏的枚舉出來.
            for (int x = 1; x <= 4; x++) {
                for (int y = 1; y <= i; y++) {
                    mapp[x][y] = cur % 2;       //用當前這個數的二進位制去填這些框框.
                    cur /= 2;
                }
            }
            num += ok(4, i);
        }
        printf("%d%c",num,i==10?'\n':',');
    }
}

// 所以對於二進位制列舉的, 請多想想!!!

相關推薦

HDU 6172 and HDU 6185 線性 + 思維 + 板子

這兩道題都是給的線性遞推式(輸入只有一個未知數n), 那麼我寫這個部落格的目的就是儲存一個超強模板, 可以解決任何線性遞推式. 這個板子是我從百度之星複賽上”偷”的杜教的板子. 所以我們現在要做的是用絕對正確的方法求出遞推式的前幾項. 然後扔進這個板子就可以了.

poj 2096 Collecting Bugs 概率DP逆向求期望

tdi cor ros quick -a sim total 3.0 pla Collecting Bugs Time Limit: 10000MS Memory Limit: 64000K Total Submissions

HDU - 6172:Array Challenge (BM線性)

oid bit pan gin https challenge pre linear res 題意:給出,三個函數,h,b,a,然後T次詢問,每次給出n,求sqrt(an); 思路:不會推,但是感覺a應該是線性的,這個時候我們就可以用BM線性遞推,自己求出前幾項,然後

HDU 6222 Heron and His Triangle ( 2017 ICPC瀋陽, 線性 + 大數)

A triangle is a Heron’s triangle if it satisfies that the side lengths of it are consecutive integers t&#8722;1, t, t+ 1 and thatits

HDU 5863 cjj's string game ( 16年多校10 G 題、矩陣快速冪優化線性DP )

sca 組合數 矩陣 spl blank mage acm 組合 str 題目鏈接 題意 : 有種不同的字符,每種字符有無限個,要求用這k種字符構造兩個長度為n的字符串a和b,使得a串和b串的最長公共部分長度恰為m,問方案數 分析 : 直覺是DP 不過當時看到 n 很

hdu 6185 Covering 求式的板子

CS Course Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 0 Accepted

HDU--3829--Cat VS Dog最大點獨立集

sin ext path lin 匹配 pat app targe anim 鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3829 題意:動物園有n條狗,m頭貓,p個小孩。每一個小孩有一個喜歡的動物和討厭的動物,如今動物園要

HDU 2045 LELE的RPG難題(

%d out miss rpg 方式 最終 desc ont != 不容易系列之(3)—— LELE的RPG難題 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768

HDU 1207 漢諾塔II (

return main 世界 個數 也會 來源 esp 一次 移動 經典的漢諾塔問題經常作為一個遞歸的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。上帝命令

BM線性模板黑科技

#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <st

杜教BM模板 線性ACM-ICPC 2018 焦作賽區網路預賽 L. Poor God Water

 L. Poor God Water God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells him that some sequence

焦作預選賽L題杜教線性 模板

真香,真好用,只許給出前幾項你就會體驗前所未有的快樂。 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #inclu

hdu 1166 敵兵佈陣單點更新 區間查詢樹狀陣列

敵兵佈陣 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 126415    Accepted Submissi

HDU 6273 Master of GCD 差分思想+快速冪

思路: 記錄乘2次數最小的值和乘3次數最小的值再用快速冪求解這個大多數人都能想到,比較棘手的就是n和m都是10^5,如果某個區間一個個的遍歷去加1的話,會超時;這裡用到了差分思想; 假設對兩個區間操作  1 到 5 ,+1, 2 到 3, + 1,那麼有  1

BZOJ4161Shlw loves matrixI (常係數齊次線性

【BZOJ4161】Shlw loves matrixI (常係數齊次線性遞推) 題面 BZOJ 題解 \(k\)很小,可以直接暴力多項式乘法和取模。 然後就是常係數齊次線性遞推那套理論了,戳這裡 #include<iostream> #include<cstdio> #in

BZOJ4944NOI2017泳池 概率DP 常係數線性 特徵多項式 多項式取模

題目大意   有一個1001×n1001×n的的網格,每個格子有qq的概率是安全的,1−q1−q的概率是危險的。   定義一個矩形是合法的當且僅當: 這個矩形中每個格子都是安全的 必須緊貼網格的下邊界   問你最大的合法子矩形大小

XSY2730Ball 多項式exp 多項式ln 多項式開根 常係數線性 DP

題目大意   一行有n個球,現在將這些球分成k 組,每組可以有一個球或相鄰兩個球。一個球只能在至多一個組中(可以不在任何組中)。求對於1≤k≤m的所有k分別有多少種分組方法。   答案對998244353取模。   n≤109,m<219 題解

HDU - 1465 - 不容易系列之一(,)

大家常常感慨,要做好一件事情真的不容易,確實,失敗比成功容易多了! 做好“一件”事情尚且不易,若想永遠成功而總從不失敗,那更是難上加難了,就像花錢總是比掙錢容易的道理一樣。 話雖這樣說,我還是要告訴大家,要想失敗到一定程度也是不容易的。比如,我高中的時候,就有一個神奇的女生,在英語考試的時候

FFT加速特徵多項式解線性hdu4914

上一篇http://blog.csdn.net/huyuncong/article/details/18184873 雖然是FFT加速,但其實這道題限制挺強的,首先特徵多形式的次數雖然上萬,但是遞推式只涉及到2項,因此初項其實可以線性推出,而且模很小隻有119,因此FFT中

HDU 6198 number number number找規律+矩陣快速冪

題目連結 題意:從含0的斐波那契數列中可重複的任取K個數,求這K個數的和無法形成的最小整數。 寫了幾個之後大膽的猜測i的答案是F[2∗(i+1)+1]−1… 於是矩陣快速冪…… #includ