学习了一个多项式求逆元的奥义…
考虑: B(x)C(x)=1(modxn)
移项并平方: B2(x)C2(x)−2B(x)C(x)+1=1(modx2n)
移项得到: B(x)(2C(x)−B(x)C(x))=1(modx2n)
然后可以由 modx 倍增上来,复杂度 O(nlgn)
#include
using namespace std;
const int MAXN = 400005;
const int mod = 998244353, g = 3;
int rev[MAXN];
int power(int a, int n)
{
int ans = 1, b = a;
for (int i = 0; i <= 30; i++) {
if ((n>>i)&1) ans = (long long)ans*b%mod;
b = (long long)b*b%mod;
}
return ans;
}
inline int inv(int a)
{ return power(a, mod-2); }
void NTT(int a[], int n, int flag)
{
int lg = 0;
for (int i = 1; i < n; i<<=1) lg++;
rev[0] = 0;
for (int i = 1; i < n; i++)
rev[i] = (rev[i>>1]>>1)|((i&1)<<(lg-1));
for (int i = 0; i < n; i++)
if (rev[i]for (int k = 1; k <= n; k <<= 1) {
int dw = power(g, (mod-1)/k);
if (flag == -1) dw = inv(dw);
for (int i = 0; i < n; i += k) {
int w = 1, u, v;
for (int j = 0; j < k>>1; j++) {
u = a[i+j], v = (long long)w*a[i+j+(k>>1)]%mod;
a[i+j] = (u+v)%mod, a[i+j+(k>>1)] = ((u-v)%mod+mod)%mod;
w = (long long)w*dw%mod;
}
}
}
if (flag == -1) {
int iv = inv(n);
for (int i = 0; i < n; i++)
a[i] = (long long)a[i]*iv%mod;
}
}
int tmp[MAXN+MAXN];
void get_inv(int a[], int b[], int n)
{
if (n == 1) {b[0] = inv(a[0]); return; }
get_inv(a, b, n>>1);
memcpy(tmp, a, sizeof(int)*n), memset(tmp+n, 0, sizeof(int)*n);
NTT(tmp, n<<1, 1), NTT(b, n<<1, 1);
for (int i = 0; i < n<<1; i++) b[i] = ((1ll*b[i]*(2-1ll*tmp[i]*b[i]%mod)%mod)+mod)%mod;
NTT(b, n<<1, -1);
memset(b+n, 0, sizeof(int)*n);
// !!!
}
int A[MAXN], B[MAXN], n, m;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
int t = 1;
while (t < m) t <<= 1;
get_inv(A, B, t);
for (int i = 0; i < m; i++)
printf("%d ", (B[i]+mod)%mod);
return 0;
}
设方案数的生成函数为 G(z) ,集合 C 的生成函数为 C(z) 。考虑一棵二叉树的形态。他要么只有一个点,要么由一个点和两个子数构成。则:
解得: G(z)=1±1−4C(z)√C(z)
考虑正负号。题目中有 c[i]≥1 ,所以右边的分母常数项为0,当且仅当分子常数项也为0解才正确。因此要取符号。
分子有理化,得到:
#include
using namespace std;
const int MAXN = (1<<18)+1;
const int mod = 998244353, g = 3;
int rev[MAXN];
int power(int a, int n)
{
int ans = 1, b = a;
for (register int i = 0; i <= 30; i++) {
if ((n>>i)&1) ans = (long long)ans*b%mod;
b = (long long)b*b%mod;
}
return ans;
}
int inv(int a)
{ return power(a, mod-2); }
void NTT(int a[MAXN], int n, int flag)
{
int lg = 0;
for (register int i = 1; i < n; i <<= 1) lg++;
rev[0] = 0;
for (register int i = 0; i < n; i++)
rev[i] = (rev[i>>1]>>1)|((i&1)<<(lg-1));
for (register int i = 0; i < n; i++)
if (rev[i]for (register int k = 1; k <= n; k <<= 1) {
int dw = power(g, (mod-1)/k);
if (flag == -1) dw = inv(dw);
for (register int i = 0; i < n; i += k) {
int w = 1, u, v;
for (register int j = 0; j < k>>1; j++) {
u = a[i+j], v = (long long)w*a[i+j+(k>>1)]%mod;
a[i+j] = (u+v)%mod, a[i+j+(k>>1)] = ((u-v)%mod+mod)%mod, w = (long long)w*dw%mod;
}
}
}
if (flag == -1) {
int k = inv(n);
for (register int i = 0; i < n; i++)
a[i] = (long long)a[i]*k%mod;
}
}
int tmp_inv[MAXN*2];
void get_inv(int a[], int b[], int n)
{
if (n == 1) { b[0] = inv(a[0]); return;}
get_inv(a, b, n>>1);
memcpy(tmp_inv, a, sizeof(int)*n), memset(tmp_inv+n, 0, sizeof(int)*n);
NTT(tmp_inv, n<<1, 1), NTT(b, n<<1, 1);
for (register int i = 0; i < n<<1; i++)
b[i] = (1ll*b[i]*(2-1ll*tmp_inv[i]*b[i]%mod)%mod+mod)%mod;
NTT(b, n<<1, -1);
memset(b+n, 0, sizeof(int)*n);
}
int tmp_sqrt[MAXN]; // b^{-1}
int tmp_ntt[MAXN];
void get_sqrt(int a[], int b[], int n)
{
if (n == 1) {b[0] = 1; return; }
get_sqrt(a, b, n>>1), memset(tmp_sqrt, 0, sizeof(int)*n*2);
get_inv(b, tmp_sqrt, n);
memcpy(tmp_ntt, a, sizeof(int)*n), memset(tmp_ntt+n, 0, sizeof(int)*n);
NTT(tmp_sqrt, n<<1, 1), NTT(tmp_ntt, n<<1, 1), NTT(b, n<<1, 1);
int val = inv(2);
for (register int i = 0; i < n<<1; i++)
b[i] = (1ll*b[i]*val%mod+1ll*tmp_ntt[i]*tmp_sqrt[i]%mod*val%mod)%mod;
NTT(b, n<<1, -1), memset(b+n, 0, sizeof(int)*n);
}
int C[MAXN], n, m, d[MAXN], e[MAXN], ts[MAXN];
inline int read()
{
int a = 0, c;
do c = getchar(); while(!isdigit(c));
while(isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
int main()
{
scanf("%d%d", &n, &m);
int t = 1;
while (t <= m) t <<= 1;
for (register int i = 1; i <= n; i++)
C[read()]++;
for (register int i = 0; i < t; i++)
C[i] = ((-4ll*C[i])%mod+mod)%mod;
C[0]++;
get_sqrt(C, d, t);
d[0]++;
get_inv(d, e, t);
for (register int i = 1; i <= m; i++)
printf("%d\n", 2ll*e[i]%mod);
return 0;
}
学习一个线性递推求逆元
http://blog.miskcoo.com/2014/09/linear-find-all-invert
#include
using namespace std;
const int MAXN = 1000005;
const int mod = 1e9+7;
int f[MAXN];
int n, m;
int facd[MAXN], ifac[MAXN], inv_num[MAXN];
void init()
{
inv_num[1] = 1;
for (int i = 2; i < MAXN; i++)
inv_num[i] = (-(long long)(mod/i)*inv_num[mod%i]%mod+mod)%mod;
facd[0] = 1, ifac[0] = 1;
for (int i = 1; i < MAXN; i++) facd[i] = (long long)facd[i-1]*i%mod;
for (int i = 1; i < MAXN; i++) ifac[i] = (long long)ifac[i-1]*inv_num[i]%mod;
// for (int i = 1; i < 10; i++) cout << facd[i] << " "; puts("");
f[0] = 1, f[1] = 0, f[2] = 1;
for (int i = 3; i < MAXN; i++) f[i] = (long long)(i-1)*((f[i-1]+f[i-2])%mod)%mod;
}
inline int choose(int n, int m)
{ return (long long)facd[n]*ifac[m]%mod*ifac[n-m]%mod; }
inline int query(int n, int m)
{ return (long long)choose(n, m)*f[n-m]%mod; }
int main()
{
int T; scanf("%d", &T);
init();
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
if (n < m) puts("0");
else printf("%d\n", query(n, m));
}
return 0;
}
考虑加入 L,R 之后原数列的差分数列 ⟨a0,a2,…,an⟩ ,必然满足:
用隔板法分析可知长度为 n 的方案数为:
总方案数为:
用上指标求和:
然后用线性递推求逆元,lucas定理求组合数即可。lucas公式为:
#include
using namespace std;
int T, N, L, R;
const int mod = 1e6+3;
int fac[mod], ifac[mod];
int inv[mod];
inline int choose(int n, int m)
{
if (n < m) return 0;
return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int lucas(int n, int m, int P)
{
if (m == 0) return 1;
return lucas(n/P, m/P, P)*1ll*choose(n%P, m%P)%mod;
}
int main()
{
inv[1] = 1;
for (int i = 2; i < mod; i++) inv[i] = (-1ll*mod/i%mod*inv[mod%i]%mod+mod)%mod;
// for (int i = 1; i <= 10; i++) cerr << 1ll*i*inv[i]%mod << endl;
fac[0] = 1, ifac[0] = 1;
for (int i = 1; i < mod; i++) fac[i] = (long long)fac[i-1]*i%mod;
for (int i = 1; i < mod; i++) ifac[i] = (long long)ifac[i-1]*inv[i]%mod;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &N, &L, &R);
printf("%d\n", (lucas(N+(R-L)+1, N, mod)-1+mod)%mod);
}
return 0;
}
神题…完全不会
http://hzwer.com/4639.html
#include
using namespace std;
const int MAXN = 506, MAXM = 100005;
struct node {
int to, next, flow, cost, neg;
} edge[MAXM*10];
int head[MAXN], top = 0;
inline void push(int i, int j, int f, int c)
{
// printf("%d--%d--%d->%d\n", i, f, c, j);
++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top;
++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top;
}
const int S = MAXN-2, T = MAXN-3, SS = MAXN-4, ST = MAXN-5;
const int inf = 1e9;
int n, m, k;
void push_lim(int i, int j, int f)
{
push(SS, j, f, 0), push(i, ST, f, 0);
push(i, j, inf, 0);
}
#define IN 0
#define OUT 1
int dis[MAXN], vis[MAXN], pre[MAXN], pre_edge[MAXN];
queue<int> que;
bool spfa(int &flow, int &cost)
{
for (int i = 0; i < MAXN; i++) dis[i] = inf;
// cout << dis[1] << endl;
memset(vis, 0, sizeof vis);
dis[SS] = 0, vis[SS] = 1, que.push(SS);
while (!que.empty()) {
int nd = que.front(); que.pop();
// cout << nd << endl;
//system("sleep 0.5");
vis[nd] = 0;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to, f = edge[i].flow, c = edge[i].cost;
if (!f || dis[to] <= dis[nd]+c) continue;
dis[to] = dis[nd]+c, pre[to] = nd, pre_edge[to] = i;
if (!vis[to])
vis[to] = 1, que.push(to);
}
}
if (dis[ST] >= inf) return 0;
int maxf = INT_MAX;
for (int i = ST; i != SS; i = pre[i]) maxf = min(maxf, edge[pre_edge[i]].flow);
for (int i = ST; i != SS; i = pre[i]) {
edge[pre_edge[i]].flow -= maxf;
edge[edge[pre_edge[i]].neg].flow += maxf;
}
flow += maxf, cost += maxf*dis[ST];
return 1;
}
void mcf(int &flow, int &cost)
{
push(T, S, inf, 0);
flow = cost = 0;
while (spfa(flow, cost));
}
inline int number(int nd, int id)
{
if (nd == 0) return n+n+1;
return nd+id*n;
}
int pw[MAXN], dd[MAXN], tp = 0;
int g[155][155];
int main()
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
g[i][j] = inf;
for (int i = 0; i <= n; i++) g[i][i] = 0;
for (int i = 1; i <= m; i++) {
int u, v, d;
scanf("%d%d%d", &u, &v, &d);
g[u][v] = g[v][u] = min(g[u][v], d);
}
for (int k = 0; k <= n; k++)
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++) {
if (k <= i && k <= j)
g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
}
for (int i = 0; i <= n; i++)
for (int j = i+1; j <= n; j++)
if (g[i][j] < inf)
push(number(i, OUT), number(j, IN), inf, g[i][j]);
push(S, number(0, IN), k, 0);
for (int i = 1; i <= n; i++) {
push_lim(number(i, IN), number(i, OUT), 1);
push(number(i, OUT), T, inf, 0);
}
push(number(n, OUT), T, k, 0);
int f = 0, cost = 0;
// puts("---");
mcf(f, cost);
cout << cost << endl;
return 0;
}
hash水过去了…
正解是AC自动机,利用fail树转移。
#include
using namespace std;
const int MAXN = 1000005;
typedef unsigned long long ull;
set st;
int dp[MAXN];
int n, m;
ull hash_val(char str[])
{
int len = strlen(str);
ull ans = 0;
for (int i = 0; i < len; i++)
ans = ans*31+str[i]-'a'+1;
return ans;
}
char str[25], s[MAXN];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", str);
st.insert(hash_val(str));
// cout << hash_val(str) << endl;
}
for (int i = 1; i <= m; i++) {
memset(dp, 0, sizeof dp);
dp[0] = 1;
scanf("%s", s+1);
int len = strlen(s+1);
for (int j = 1; j <= len; j++) {
ull hav = 0, bs = 1;
// cout << j << endl;
for (int k = j; k >= max(1, j-9); k--) {
hav = hav+bs*(s[k]-'a'+1);
// cout << s[k] << " " << hav << endl;
bs = bs*31;
if (st.count(hav)) {
// printf("%d --> %d\n", k-1, j);
dp[j] |= dp[k-1];
}
}
}
int flag = 0;
for (int j = len; j >= 1; j--) {
if (dp[j]) {
printf("%d\n", j);
flag = 1;
break;
}
}
if (!flag) puts("0");
}
return 0;
}
AC自动机解法:
#include
using namespace std;
const int MAXN = 600;
int chl[MAXN][26], fail[MAXN], fin[MAXN], top = 0;
int root = 0;
void push(int &nd, const char *str, int len = 0)
{
if (!nd) nd = ++top;
if (*str == '\0') fin[nd] = len;
else push(chl[nd][*str-'a'], str+1, len+1);
}
queue<int> que;
void init()
{
que.push(root), fail[root] = 0;
while (!que.empty()) {
int nd = que.front(); que.pop();
for (int i = 0; i < 26; i++) {
if (!chl[nd][i]) continue;
int p = fail[nd];
while (p && !chl[p][i]) p = fail[p];
if (p) fail[chl[nd][i]] = chl[p][i];
else fail[chl[nd][i]] = root;
que.push(chl[nd][i]);
}
}
}
char s[30], str[1000005];
int n, m;
int dp[1000005];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", s);
push(root, s);
}
init();
for (int i = 1; i <= m; i++) {
memset(dp, 0, sizeof dp);
dp[0] = 1;
scanf("%s", str+1);
int l = strlen(str+1);
int nd = root;
for (int i = 1; i <= l; i++) {
while (nd && !chl[nd][str[i]-'a']) nd = fail[nd];
if (!nd) nd = root;
else nd = chl[nd][str[i]-'a'];
for (int p = nd; p; p = fail[p])
if (fin[p])
dp[i] |= dp[i-fin[p]];
}
int flag = 0;
for (int i = l; i >= 1; i--) {
if (dp[i]) {
flag = 1;
printf("%d\n", i);
break;
}
}
if (!flag) printf("0\n");
}
return 0;
}
直接暴力就过了???
什么鬼
#include
using namespace std;
const int MAXN = 100005, MAXS = 505;
struct node {
int to, next;
} edge[MAXN];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }
int height[MAXN];
int dp[MAXN][MAXS];
long long ans[MAXN*5];
void dfs(int nd)
{
height[nd] = 0, dp[nd][0] = 1;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
dfs(to);
for (int j = 0; j <= height[nd]; j++)
for (int k = 0; k <= height[to]; k++)
ans[j^(k+1)] += (long long)dp[nd][j]*dp[to][k];
height[nd] = max(height[nd], height[to]+1);
for (int j = 0; j <= height[to]; j++)
dp[nd][j+1] += dp[to][j];
}
}
int n;
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; i++) {
int p; scanf("%d", &p);
push(p, i);
}
dfs(1);
for (int i = 0; ans[i]; i++)
printf("%lld\n", ans[i]);
return 0;
}
直接平衡树维护即可。
然而splay疯狂TLE,treap轻松AC…
splay(TLE):
#include
using namespace std;
const int MAXN = 100005;
int chl[MAXN][2], fa[MAXN], dat[MAXN], max_val[MAXN], top = 0;
int siz[MAXN];
int root = 0;
inline void update(int nd)
{
if (!nd) return;
max_val[nd] = dat[nd];
max_val[nd] = max(max(max_val[nd], max_val[chl[nd][0]]), max_val[chl[nd][1]]);
siz[nd] = siz[chl[nd][0]]+siz[chl[nd][1]]+1;
}
inline void zig(int nd)
{
int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;
int son = chl[nd][tp^1];
fa[son] = (son!=0)*p, chl[g][tg] = (g!=0)*nd;
fa[nd] = g, fa[p] = nd, chl[nd][tp^1] = p, chl[p][tp] = son;
update(p), update(nd);
}
void splay(int nd, int tar_fa = 0)
{
int p, g, tp, tg;
while (fa[nd] != tar_fa) {
p = fa[nd], g = fa[p];
tp = chl[p][0] != nd, tg = chl[g][0] != p;
if (g == tar_fa) { zig(nd); break; }
if (tp == tg) zig(p), zig(nd);
else zig(nd), zig(nd);
}
if (tar_fa == 0) root = nd;
}
int rank_of(int nd)
{
splay(nd);
return siz[chl[nd][0]]+1;
}
inline int find_kth(int k)
{
int nd = root;
while (1) {
if (siz[chl[nd][0]]+1 == k) break;
if (siz[chl[nd][0]]+1 > k) nd = chl[nd][0];
else nd = chl[nd][1], k -= siz[chl[nd][0]]+1;
}
return nd;
}
void insert_in(int k)
{
if (!root)
{ root = ++top, dat[root] = 1, update(root); }
else if (k == 0)
{ splay(find_kth(1)), chl[root][0] = ++top, fa[top] = root, dat[top] = 1, update(top), update(root); }
else if (k == siz[root])
{ splay(find_kth(k)), chl[root][1] = ++top, fa[top] = root, dat[top] = max_val[root]+1, update(top), update(root); }
else {
int nd = find_kth(k), r = find_kth(k+1);
splay(nd), splay(r, root);
chl[r][0] = ++top, fa[top] = r, dat[top] = max(max_val[chl[root][0]], dat[root])+1;
update(top), update(r), update(root);
}
}
int n;
int k;
inline int read()
{
int a = 0, c;
do c = getchar(); while (!isdigit(c));
while (isdigit(c)) {
a = a*10+c-'0';
c = getchar();
}
return a;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
k = read();
insert_in(k);
printf("%d\n", max_val[root]);
}
return 0;
}
复习虚树。
#include
using namespace std;
const int MAXN = 250005;
const long long inf = 23333333333333ll;
struct node {
int to, next, dis;
} edge[MAXN*2], edge_it[MAXN*2];
int head[MAXN], top = 0;
int head_it[MAXN], top_it = 0;
inline void push(int i, int j, int d)
{ edge[++top] = (node){j, head[i], d}, head[i] = top; }
int it_kill_list[MAXN], list_top = 0;
inline void push_it(int i, int j, int d)
{
// printf("%d--%d-->%d\n", i, d, j);
if (!head_it[i]) it_kill_list[++list_top] = i;
edge_it[++top_it] = (node) {j, head_it[i], d}, head_it[i] = top_it;
}
int depth[MAXN];
long long len[MAXN];
int fa[MAXN][21], dfn[MAXN], dfn_top = 0;
int out[MAXN], n, m;
void dfs(int nd, int f)
{
fa[nd][0] = f, dfn[nd] = ++dfn_top;
// cerr << nd << " " << dfn_top << endl;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == f) continue;
depth[to] = depth[nd]+1, len[to] = min(len[nd], (long long)edge[i].dis);
dfs(to, nd);
}
out[nd] = dfn_top;
}
void init_lca()
{
for (int j = 1; j <= 20; j++)
for (int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j-1]][j-1];
}
int lca(int a, int b)
{
if (depth[a] < depth[b]) swap(a, b);
for (int d = depth[a]-depth[b], i = 0; i <= 20; i++)
if ((d>>i)&1)
a = fa[a][i];
if (a == b) return a;
for (int i = 20; i >= 0; i--)
if (fa[a][i] != fa[b][i])
a = fa[a][i], b = fa[b][i];
return fa[a][0];
}
bool cmp(int a, int b)
{ return dfn[a] < dfn[b]; }
int stk[MAXN], stk_top = 0;
int in_stk[MAXN];
int mark[MAXN];
inline bool in_subtree(int nd, int T)
{ return dfn[nd] >= dfn[T] && dfn[nd] <= out[T];}
void build_tree(vector<int> &pt)
{
sort(pt.begin(), pt.end(), cmp);
// puts("Building tree");
// for (int i = 0; i < pt.size(); i++)
// cerr << dfn[pt[i]] << " ";
// cerr << endl;
for (int i = 0; i < pt.size(); i++)
mark[pt[i]] = 1;
mark[1] = 0;
for (int i = 0; i < pt.size(); i++) {
// cerr << pt[i] << endl;
if (!stk_top) stk[++stk_top] = pt[i], in_stk[pt[i]] = 1;
else {
while (stk_top > 1 && !in_subtree(pt[i], stk[stk_top-1]) &&
!in_subtree(pt[i], stk[stk_top])) {
in_stk[stk[stk_top]] = 0;
push_it(stk[stk_top-1], stk[stk_top], len[stk[stk_top]]);
stk_top--;
// cerr << stk_top << endl;
}
if (in_subtree(pt[i], stk[stk_top])) stk[++stk_top] = pt[i], in_stk[pt[i]] = 1;
else {
int bro = stk[stk_top--], t = lca(bro, pt[i]);
if (!stk_top || t != stk[stk_top]) stk[++stk_top] = t, in_stk[t] = 1;
push_it(stk[stk_top], bro, len[bro]), stk[++stk_top] = pt[i], in_stk[pt[i]] = 1;
}
}
}
while (stk_top > 1) {
// cerr << stk_top << endl;
push_it(stk[stk_top-1], stk[stk_top], len[stk[stk_top]]);
stk_top--;
}
stk_top = 0;
}
long long dfs_dp(int nd, int f)
{
if (mark[nd]) return inf;
long long ans = 0;
for (int i = head_it[nd]; i; i = edge_it[i].next) {
int to = edge_it[i].to, d = edge_it[i].dis;
// cerr << nd << "-->" << d <<"-->" << to << endl;
if (to == f) continue;
ans += min((long long)d, dfs_dp(to, nd));
if (ans > inf) ans = inf;
}
return ans;
}
void kill_tree(vector<int> &vec)
{
int num = vec.size();
for (int i = 1; i <= list_top; i++)
head_it[it_kill_list[i]] = 0;
list_top = 0;
top_it = 0;
for (int i = 0; i < num; i++)
mark[vec[i]] = 0;
}
vector<int> vec;
int main()
{
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v, d;
scanf("%d%d%d", &u, &v, &d);
push(u, v, d), push(v, u, d);
}
scanf("%d", &m);
for (int i = 1; i <= n; i++) len[i] = inf;
dfs(1, 0), init_lca();
for (int i = 1; i <= m; i++) {
int t, u; scanf("%d", &t);
vec.clear();
vec.push_back(1);
while (t--) {
scanf("%d", &u);
vec.push_back(u);
}
build_tree(vec);
printf("%lld\n", dfs_dp(1, 0));
kill_tree(vec);
}
return 0;
}
数据范围告诉我们是矩阵乘法..
但是怎么限制不走回头路呢…
一种可行的方案是边点互换,然后在点上转移 t 次就变成了在边上转移 t−1 次,然后就可以矩阵了。
#include
using namespace std;
const int MAXN = 125, mod = 45989;
int n, m, t, a, b;
struct node {
int from, to, neg;
} edge[MAXN*2];
int top = 0;
struct matrix {
int A[MAXN][MAXN];
void clear()
{ memset(A, 0, sizeof A); }
matrix()
{ clear(); }
friend matrix operator * (const matrix &a, const matrix &b)
{
matrix ret;
for (int i = 1; i <= top; i++)
for (int j = 1; j <= top; j++)
for (int k = 1; k <= top; k++)
(ret.A[i][j] += a.A[i][k]*b.A[k][j]%mod) %= mod;
return ret;
}
} g;
matrix power(matrix a, int p)
{
matrix ans;
for (int i = 1; i <= top; i++)
ans.A[i][i] = 1;
for (int i = 0; i < 31; i++) {
if ((p>>i)&1) ans = ans*a;
a = a*a;
}
return ans;
}
inline void push(int x, int y)
{
++top, edge[top] = (node){x, y, top+1};
++top, edge[top] = (node){y, x, top-1};
}
int c[MAXN], y[MAXN];
void build()
{
for (int i = 1; i <= top; i++) {
for (int j = 1; j <= top; j++) {
if (i != j && j != edge[i].neg && edge[i].to == edge[j].from)
g.A[i][j] = 1;
}
if (edge[i].from == a) c[i]++;
}
}
int main()
{
scanf("%d%d%d%d%d", &n, &m, &t, &a, &b);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
push(u, v);
}
build();
g = power(g, t-1);
for (int i = 1; i <= top; i++) {
for (int j = 1; j <= top; j++)
(y[i] += c[j]*g.A[j][i]) %= mod;
}
int ans = 0;
for (int i = 1; i <= top; i++)
if (edge[i].to == b)
(ans += y[i]) %= mod;
printf("%d\n", ans);
return 0;
}
给定一棵树,点有权值。删掉一个点会使得该节点的权值和孩子挂到父亲,求最多删去多少个点。
考虑如下的贪心策略,递归的做各个子树,然后贪心的删除尽可能多的儿子。
这个贪心是正确的,因为不可能为了在上面删除节点而放弃删除下面的。因为在越高的地方删除难度越大..
#include
using namespace std;
const int MAXN = 2000005;
struct node {
int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node){j, head[i]}, head[i] = top; }
int n, m;
int chl[MAXN], c[MAXN];
int cnt = 0;
bool cmp(const int a, const int b)
{ return chl[a]+c[a] < chl[b]+c[b]; }
void dfs(int nd)
{
vector<int> vec;
for (int i = head[nd]; i; i = edge[i].next)
dfs(edge[i].to), vec.push_back(edge[i].to);
sort(vec.begin(), vec.end(), cmp);
for (int i = 0; i < vec.size(); i++) {
if (c[nd]+chl[nd]-1+chl[vec[i]]+c[vec[i]] > m) break;
c[nd] += c[vec[i]], chl[nd] += chl[vec[i]]-1;
cnt++;
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
for (int i = 1; i <= n; i++) {
scanf("%d", &chl[i]);
for (int j = 1; j <= chl[i]; j++) {
int v; scanf("%d", &v);
push(i, ++v);
}
}
dfs(1);
printf("%d\n", cnt);
return 0;
}
树链的并…
luogu数据太弱了吧…边界根本卡不住..
小黄鸭调试法get
#include
using namespace std;
const int MAXN = 200005;
struct node {
int to, next;
long long dis;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j, long long d)
{ edge[++top] = (node){j, head[i], d}, head[i] = top; }
int dfn[MAXN], dfn_top = 0;
int depth[MAXN];
long long len[MAXN];
int fa[MAXN][21];
int n, m;
void dfs(int nd, int f)
{
dfn[nd] = ++dfn_top;
depth[nd] = depth[f]+1, fa[nd][0] = f;
for (int i = head[nd]; i; i = edge[i].next) {
if (edge[i].to != f)
len[edge[i].to] = len[nd]+edge[i].dis, dfs(edge[i].to, nd);
}
}
void init()
{
for (int j = 1; j <= 20; j++)
for (int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j-1]][j-1];
}
int lca(int a, int b)
{
if (depth[a] < depth[b]) swap(a, b);
for (int i = 0, d = depth[a]-depth[b]; i <= 20; i++)
if ((d>>i)&1)
a = fa[a][i];
if (a == b) return a;
for (int i = 20; i >= 0; i--)
if (fa[a][i] != fa[b][i])
a = fa[a][i], b = fa[b][i];
return fa[a][0];
}
long long dis(int a, int b)
{
int c = lca(a, b);
return len[a]+len[b]-2*len[c];
}
class cmp {
public:
bool operator () (const int &a, const int &b) const
{ return dfn[a] < dfn[b]; }
};
set<int, cmp, allocator<int> > st;
long long ans = 0;
void insert(int nd)
{
if (st.empty()) { st.insert(nd); return; }
set<int, cmp, allocator<int> >::iterator i = st.upper_bound(nd), j;
if (i == st.begin())
j = st.end(), j--;
else if (i == st.end())
i--, j = st.begin();
else j = i, j--;
// cerr << *i << " " << *j << " -- " << nd << " " << dis(2, 3) << endl;
ans -= dis(*i, *j), ans += dis(*i, nd), ans += dis(*j, nd);
st.insert(nd);
}
void del(int nd)
{
if (st.size() == 1) { st.erase(nd); return; }
set<int, cmp, allocator<int> >::iterator i = st.lower_bound(nd), l, r;
if (i == st.begin())
l = st.end(), l--, r = i, r++;
else if (i == --st.end())
r = st.begin(), l = i, l--;
else l = i, l--, r = i, r++;
ans -= dis(*i, *l), ans -= dis(*i, *r), ans += dis(*l, *r);
st.erase(nd);
}
void deal(int nd)
{
if (st.count(nd)) del(nd);
else insert(nd);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i < n; i++) {
int u, v;
long long d;
scanf("%d%d%lld", &u, &v, &d);
push(u, v, d), push(v, u, d);
}
dfs(1, 0), init();
for (int i = 1; i <= m; i++) {
int t; scanf("%d", &t);
deal(t);
printf("%lld\n", ans);
}
return 0;
}
坠喜欢的网络流模型!
在无源汇可行流中,一个回路是一个增广轨。对于这个题来说,显然要将图变成不相交的环,每个节点拆点并限制流量为1,改变方向需要费用,跑无源汇最小费用可行流即可。
#include
using namespace std;
const int MAXN = 20;
int g[MAXN][MAXN];
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
int n, m;
char str[MAXN];
struct node {
int to, next, flow, cost, neg;
} edge[MAXN*MAXN*100];
int head[MAXN*MAXN*2], top = 0;
inline void push(int i, int j, int f, int c)
{
// if (i == 18)
// printf("%d--%d,%d-->%d\n", i, f, c, j);
++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top;
++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top;
}
const int S = MAXN*MAXN*2-4, T = S+1;
inline void push_lim(int i, int j, int flim)
{
push(S, j, flim, 0), push(i, T, flim, 0);
}
int dis[MAXN*MAXN*2], vis[MAXN*MAXN*2];
queue<int> que;
int pre[MAXN*MAXN*2], pre_edge[MAXN*MAXN*2];
const int inf = 23333333;
bool spfa(int &flow, int &cost)
{
memset(dis, 127/3, sizeof dis);
memset(vis, 0, sizeof vis);
vis[S] = 1, dis[S] = 0, que.push(S);
while (!que.empty()) {
int nd = que.front(); que.pop(), vis[nd] = 0;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to, f = edge[i].flow, c = edge[i].cost;
if (!f || dis[to] <= dis[nd]+c) continue;
dis[to] = dis[nd]+c, pre[to] = nd, pre_edge[to] = i;
if (!vis[to])
vis[to] = 1, que.push(to);
}
}
if (dis[T] >= inf) return 0;
int maxf = inf;
for (int i = T; i != S; i = pre[i]) maxf = min(maxf, edge[pre_edge[i]].flow);
// cerr << maxf << " " << dis[T] << endl;
for (int i = T; i != S; i = pre[i]) {
edge[pre_edge[i]].flow -= maxf;
edge[edge[pre_edge[i]].neg].flow += maxf;
// cerr << "Arg : " << pre[i] << "-->"<< i << " Dis = "<< edge[pre_edge[i]].cost << endl;
}
flow += maxf, cost += dis[T]*maxf;
return 1;
}
void mcf(int &flow, int &cost)
{
flow = cost = 0;
while (spfa(flow, cost));
}
inline int number(int i, int j, int id)
{ return (i-1)*m+j+id*(n*m); }
#define IN 0
#define OUT 1
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", str+1);
for (int j = 1; j <= m; j++) {
switch(str[j]) {
case 'U' : g[i][j] = UP; break;
case 'L' : g[i][j] = LEFT; break;
case 'R' : g[i][j] = RIGHT; break;
case 'D' : g[i][j] = DOWN; break;
}
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
push_lim(number(i, j, IN), number(i, j, OUT), 1);
if (g[i][j] == UP) {
push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 0);
push(number(i, j, OUT), number(i1:1, j, IN), 1, 1);
push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 1);
push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 1);
} else if (g[i][j] == DOWN) {
push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 1);
push(number(i, j, OUT), number(i1:1, j, IN), 1, 0);
push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 1);
push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 1);
} else if (g[i][j] == LEFT) {
push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 1);
push(number(i, j, OUT), number(i1:1, j, IN), 1, 1);
push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 0);
push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 1);
} else if (g[i][j] == RIGHT) {
push(number(i, j, OUT), number(i>1?i-1:n, j, IN), 1, 1);
push(number(i, j, OUT), number(i1:1, j, IN), 1, 1);
push(number(i, j, OUT), number(i, j>1?j-1:m, IN), 1, 1);
push(number(i, j, OUT), number(i, j<m?j+1:1, IN), 1, 0);
}
}
int flow, cost;
mcf(flow, cost);
cout << cost << endl;
return 0;
}