A
題目:
標算:
另有找規律解法:先把轉化成大於等於的子集數,就是用替代。然後如果肯定無解。否則我們寫出一個的矩陣,形如倒置的楊輝三角。如下圖:
然後直接對於每一行,從左往右掃,如果減去當前格子的數後非負,就減去,然後把答案的鄰接矩陣對應位置設爲,否則直接跳到下一行。然後就完了。
時間複雜度。所以給開到,然後用高精讀入也是可以做的。(
是不是很nb。
CODE
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL w[50][50], k;
int ans[50][50], n;
int main () {
scanf("%d%lld", &n, &k);
k = (1ll<<n)-1-k;
if(k < 0) { puts("-1"); return 0; }
w[n][1] = 1;
for(int i = n-1; i; --i)
for(int j = 1; j <= n-i+1; ++j)
w[i][j] = w[i+1][j] + w[i+1][j-1];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n-i+1; ++j)
if(k >= w[i][j]) k -= w[i][j], ans[i][j] = 1;
else break;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
printf("%d%c", ans[i][j], " \n"[j==n]);
}
B
毒瘤。簡單的想法是每個質數獨立算。相當於求一個方程組的解的個數。這個可以完全揹包來算。然後是無序的,可以規定成有序算最後除以每個相同的個數的階乘。
但是要求不相等。所以必須枚舉集合劃分來容斥。,所以集合劃分方案最多爲。然後要求一個劃分的容斥係數。可以發現一個劃分的容斥係數只跟裏面每個集合的有關。那麼定義本質不同表示每個集合的排序後的序列不相同。那麼本質不同的劃分就很少了。對於每一種劃分的本質我們求出在這種本質下有多少劃分的方案和這種本質的容斥係數。那麼最後乘起來再乘上前面完全揹包求方程組解數就是答案了。
在這種本質下有多少劃分的方案可以直接。
求容斥係數要考慮一下。一個劃分的容斥係數就是裏面每個集合的容斥係數之積。那麼考慮一個集合,其實容斥係數只與集合大小有關。如果它的大小爲,容斥係數就是應該是。如果大小,那麼必須是它的所有子集合(包括自己)的容斥係數之和爲,因爲我們不容許有兩個相等。
假設表示大小爲的集合的容斥係數。那麼寫出來就是相當於是在固定一個號節點,枚舉號節點所在子集的大小,然後在個點選個,乘上這個子集合的容斥係數。剩下個點,如果那麼它的子集合係數和一定是,所以只考慮的情況。
所以,對於。可得出
那麼這道題就算完了。可以參考一下代碼:
由於卡常所以手寫了一個只有功能的。
CODE
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef vector<int> Vec;
typedef long long LL;
const int mod = 1e9 + 7;
const int MAXM = 31;
const int MAXN = 10005;
const LL p = 131;
struct Set {
pii a[MAXM]; LL hsh;
Set() { memset(a, 0, sizeof a); hsh = 0; }
inline void gethash() {
sort(a, a + 31, greater<pii>()); hsh = 0;
for(int i = 0; i < 31 && a[i].first; ++i)
hsh = (hsh * p + a[i].first) * p + a[i].second;
}
inline bool operator <(const Set &o)const { return hsh < o.hsh; }
};
map<Set,int>dp[2];
map<Vec,int>cf;
int n, m, fac[MAXM], inv[MAXM], a[MAXM];
void pre(int N) {
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= N; ++i) fac[i] = 1ll * fac[i-1] * i % mod, inv[i] = 1ll * (mod-mod/i) * inv[mod%i] % mod;
for(int i = 2; i <= N; ++i) inv[i] = 1ll * inv[i-1] * inv[i] % mod;
}
int pr[MAXN], cnt, mi[MAXN]; bool vis[MAXN];
void pre2(int N) {
for(int i = 2; i <= N; ++i) {
if(!vis[i]) pr[++cnt] = i;
for(int j = 1; j <= cnt && pr[j]*i <= N; ++j) {
vis[pr[j]*i] = 1; if(i % pr[j] == 0) break;
}
}
for(int i = 1, tmp; i <= cnt; ++i){
for(tmp = N; tmp; tmp /= pr[i]) mi[i] += tmp/pr[i];
}
}
int f[MAXN];
inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
int main () {
scanf("%d%d", &n, &m); pre(30); pre2(n);
for(int i = 1; i <= m; ++i) scanf("%d", &a[i]);
sort(a + 1, a + m + 1);
bool cur = 0; dp[cur][Set()] = 1;
Set u; int w;
for(int i = 1; i <= m; ++i) { //處理相同本質的劃分方案數
dp[cur^=1].clear();
for(map<Set,int>::iterator it = dp[cur^1].begin(); it != dp[cur^1].end(); ++it) {
w = it->second;
for(int j = 0; j < 31 && (!j || it->first.a[j-1].first); ++j) {
u = it->first; u.a[j].first += a[i], ++u.a[j].second;
u.gethash();
dp[cur][u] = add(dp[cur][u], w);
}
}
}
for(map<Set,int>::iterator it = dp[cur].begin(); it != dp[cur].end(); ++it) { //計算容斥係數
u = it->first, w = it->second;
Vec vc;
for(int i = 0; i < 31 && u.a[i].first; ++i) {
vc.push_back(u.a[i].first);
w = 1ll * w * fac[u.a[i].second-1] * ((u.a[i].second & 1) ? 1 : -1) % mod;
}
if(w < 0) w += mod;
cf[vc] = add(cf[vc], w);
}
int ans = 0;
for(map<Vec,int>::iterator it = cf.begin(); it != cf.end(); ++it) { //算上方程解的個數(完全揹包計算
Vec vc = it->first; w = it->second;
memset(f, 0, sizeof f); f[0] = 1;
for(int i = 0, len = vc.size(); i < len; ++i)
for(int j = vc[i]; j <= mi[1]; ++j)
f[j] = add(f[j], f[j-vc[i]]);
for(int i = 1; i <= cnt; ++i) w = 1ll * w * f[mi[i]] % mod;
ans = add(ans, w);
}
for(int i = 1, j; i <= m; i = j) { //去掉把ai視作有序的影響
for(j = i; j <= m && a[j] == a[i]; ++j);
ans = 1ll * ans * inv[j-i] % mod;
}
printf("%d\n", ans);
}
C
直接算一下大概每個塊的值,然後蛇形填數。加上第一個點手玩或者寫暴力隨機bfs,能得到92~93分的好成績。我只有92。不好放out文件就不放了。