炮(cannon)
【題⽬描述】 衆所周知,雙炮疊疊將是中國象棋中很厲害的⼀招必殺技。炮吃⼦時必須 隔⼀個棋⼦跳吃,即俗稱“炮打隔⼦”。 炮跟炮顯然不能在⼀起打起來,於是rly ⼀天借來了許多許多的炮在棋盤上擺了起來……他想知道,在N×M的矩形⽅格 中擺若⼲炮(可以不擺)使其互不吃到的情況下⽅案數有⼏種。 棋⼦都是相同的。
【輸⼊說明】 ⼀⾏,兩個正整數N和M。
【輸出說明】 ⼀⾏,輸出⽅案數mod 999983。
【樣例輸⼊】 1 3
【樣例輸出】 7
【數據範圍】 對於40%的數據,N<=4,M<=4 對於70%的數據,N<=100,M<=8 對於100%的數據,N<=100,M<=10
題解:
[AHOI2009中國象棋] 原題
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m;
long long f[111][111][111], ans;
long long MOD = 999983;
int main() {
freopen("cannon.in", "r", stdin);
freopen("cannon.out", "w", stdout);
scanf("%d%d", &n, &m);
f[0][0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= m; j++) {
for(int k = 0; k <= m - j; k++) {
if(k && i == 1) break;
long long & now = f[i][j][k];
now += f[i - 1][j][k];
if(j >= 1) now = (now + f[i - 1][j - 1][k] * (m - j + 1 - k)) % MOD; //µÚiÐзÅÒ»¸ö ÇÒ·ÅÔÚÁË֮ǰûÓÐÅÚµÄÒ»ÁС£¡£
if(j + 1 <= m && k >= 1) now = (now + f[i - 1][j + 1][k - 1] * (j + 1)) % MOD; //µÚiÐзÅÒ»¸ö ÇÒ·ÅÔÚÁË֮ǰÓÐÒ»¸öÅÚµÄÒ»ÁÐ
if(j >= 2) now = (now + f[i - 1] [j - 2][k] * ((m - j + 2 - k) * (m - j + 2 - k - 1) / 2)) % MOD; //µÚiÐзÅÁ½¸ö ÇÒ¶¼·ÅÔÚÁË Ã»ÓÐÅÚµÄÁÐ
if(k >= 1) now = (now + f[i - 1][j][k - 1] * (m - j - k + 1) * (j)) % MOD; //µÚiÐзÅÁ½¸ö Ò»¸ö·ÅÔÚûÓÐÅÚµÄÒ»ÁÐ Ò»¸ö·ÅÔÚÁËÓÐÒ»¸öÅÚµÄÒ»ÁÐ
if(j + 2 <= m && k >= 2) now = (now + f[i - 1][j + 2][k - 2] * ((j + 2) * (j + 2 - 1) / 2) ) % MOD; //µÚiÐзÅÁ½¸ö ¶¼·ÅÔÚÁËÓÐÒ»¸öÅÚµÄÒ»ÁÐ
now %= MOD;
// cout<<"f["<<i<<"]["<<j<<"]["<<k<<"] = "<<now<<endl;
}
}
}
for(int i = 0; i <= m; i++)
for(int j = 0; j <= m - i; j++)
ans = (ans + f[n][i][j]) % MOD;
ans %= MOD;
printf("%lld\n", ans);
}
/*
*/
三維狀態的DP
⾞(rook)
【題⽬描述】 衆所周知,⾞是中國象棋中最厲害的⼀⼦之⼀,它能吃到同⼀⾏或同⼀列 中的其他棋⼦。⾞跟⾞顯然不能在⼀起打起來,於是rly⼀天又借來了許多許多 的⾞在棋盤上擺了起來……他想知道,在N×M的矩形⽅格中擺最多個數的⾞使 其互不吃到的情況下⽅案數有⼏種。但是,由於上次擺炮擺得實在太累,他爲 了偷懶,打算增加⼀個條件:對於任何⼀個⾞A,如果有其他⼀個⾞B在它的上 ⾯(⾞B⾏號⼩於⾞A),那麼⾞A必須在⾞B的右邊(⾞A列號⼤於⾞B)。 棋⼦都是相同的。
【輸⼊說明】 ⼀⾏,兩個正整數N和M。
【輸出說明】 ⼀⾏,輸出⽅案數的末尾50位(不⾜則直接輸出)。
【樣例輸⼊】 2 2
【樣例輸出】 1
【數據範圍】 對於20%的數據,N<=10,M<=10。 對於40%的數據,N<=40,M<=40。 對於70%的數據,N<=10000,M<=10000。 對於100%的數據,N<=1000000,M<=1000000。
題解:
排列組合C(n,m) + 質因數分解(快速質因數分解John M. Pollard方法 n ^(1/4)) + 高精乘
如果 n < m, 交換一下.
一開始寫的遞推 O(m * (n - m +1)) 後來發現可以矩陣乘法, 再後來發現可以兩個sum轉換前綴和。。。。然而不是正解。。。。
注意坑點:如果答案超過50位,則前導零不能刪,若答案不超過50位,不要前導零。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define debug cout<<"Orzzzzzzzzzzzzzzzzzzzzzzzzzzz"<<endl;
long long n, m;
long long check[1111111], prime[1111111], js[1111111], ans[111];
void work(long long now, int val) {
for(int i = 1; i <= prime[0]; i++) {
long long tmp = now;
while(tmp) {
if(val) js[i] += tmp / prime[i];
else js[i] -= tmp / prime[i];
tmp /= prime[i];
}
}
}
int work_2(long long x, int y) {
while(y--) {
for(int i = 1; i <= ans[0]; i++) ans[i] *= x;
for(int i = 1; i <= ans[0] + 8 && i <= 50; i++)
if(ans[i] > 9) {
ans[i + 1] += ans[i] / 10;
ans[i] %= 10;
}
ans[0] = min(50, (int)ans[0] + 8);
while(ans[ans[0]] == 0) ans[0]--;
// while(ans[ans[0] + 1]) ans[0]++; //DEBUG!!!!²»ÒªÕâôд£¡£¡£¡£¡ 901111 »áÔÚ0´¦Ìø³ö£¡£¡£¡£¡£¡Ê¹ans¡¾0¡¿ = 4
// cout<<y<<" dfdfdfd "<<endl;
// getchar();
}
}
int main() {
freopen("rook.in", "r", stdin);
freopen("rook.out", "w", stdout);
ans[++ans[0]] = 1;
cin>>n>>m;
if(n < m) {
n += m;
m = n - m;
n -= m;
}
for(int i = 2; i <= n; i++) {
if(!check[i]) prime[++prime[0]] = i;
for(int j = 1; j <= prime[0]; j++) {
if(prime[j] * i > n) break;
check[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
work(n, 1);
work(m, 0);
work(n - m, 0);
for(int i = 1; i <= prime[0]; i++) {
if(js[i] >= 1)
work_2(prime[i], js[i]);
// cout<<js[i]<<' '<<i<<' '<<prime[i]<<endl;
}
int xx = 50;
while(ans[xx] == 0 && xx > 1) xx--;
if(ans[51]) xx = 50;
for(int i = xx; i >= 1; i--) cout<<ans[i];
return 0;
}
皇后(queen)
【題⽬描述】 衆所不知,rly現在不會玩國際象棋。但是,作爲⼀個OIer,rly當然做過⼋ 皇后問題。這⾥再囉嗦⼏句,皇后可以攻擊到同⾏同列同對⾓線,在n*n的⽅格 中擺n個皇后使其互不攻擊到,求不同的解的數量,這就是經典的n皇后問題。 現在問題推⼴到n皇后問題,這個問題對於你⽽⾔實在是⼩菜⼀疊。但因爲上⼀ 次rly把棋盤弄破了,又拿不出新的,所以rly打算難⼀點點,問題就是破棋盤上 的n皇后問題。他想知道……(你們懂的)。 棋⼦都是相同的。
【輸⼊說明】 ⼀⾏,⼀個正整數N。 接下來N⾏,每⾏N個數,要麼爲0,表⽰沒壞,要麼1,表⽰壞了。
【輸出說明】 ⼀⾏,輸出不同的解的數量。
【樣例輸⼊】 4
1 0 1 1
1 1 1 0
0 1 1 1
1 1 0 1
【樣例輸出】 1
【數據範圍】 對於40%的數據,N<=13。 對於100%的數據,N<=16。 其中有30%的數據,棋盤沒有破(你可以認爲rly又去買了⼀個新的)。
題解:
位運算的n皇后 比數組記錄可選位置要快很多
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int xie_sum[50], xie_unsum[50], hang[50], lie[50];
long long ans;
int cs[50];
int n, cnt;
long long db[32] = {0, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712};
struct node {
int x, y;
bool operator < (const node & xx) const {
return x < xx.x;
}
}ha[356];
int lb(int now) {
return now & (-now);
}
int dfs(int now, int lie, int xie_l, int xie_r) {
int nn = cs[now] & lie & xie_l & xie_r;
if(now > n) {
ans++;
return 0;
}
while(nn) {
int tmp = lb(nn);
int x1 = lie, x2 = xie_l, x3 = xie_r;
x1 ^= tmp;
x2 ^= tmp;
x3 ^= tmp;
x2 <<= 1;
x2 |= 1;
x3 >>= 1;
x3 |= 1 << (n - 1);
if(x2 >> n) x2 -= 1 << n;
// cout<<now<<' '<<nn<<' '<<tmp<<' '<<x1<<' '<<x2<<' '<<x3<<" dfdfdfdf "<<endl;
nn -= tmp;
dfs(now + 1, x1, x2, x3);
}
}
int main() {
freopen("queen.in", "r", stdin);
freopen("queen.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int xx;
scanf("%d", &xx);
if(!xx) cs[i] |= (1 << (n - j));
}
// cout<<"cs["<<i<<"] = "<<cs[i]<<endl;
}
// sort(ha + 1, ha + 1 + cnt);
// if(cnt == n * n) {
// cout<<db[n]<<endl;
// return 0;
// }
dfs(1, (1 << n) - 1, (1 << n) - 1, (1 << n) - 1);
cout<<ans<<endl;
return 0;
}
/*
4
1 0 1 1
1 1 1 0
0 1 1 1
1 1 0 1
long long db[32] = {0, 1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712}
4
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
14
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
*/