CSP-S模拟33
今天是挂分场,总计挂分 132 tps。。。
T1:Divisors
大简单题。
这里粘一个结论,然后直接暴力做就行了。
结论
$ [1,1e5] 内 因子个数max = 128 (83160) $
$ [1,1e6] 内 因子个数max = 240 (720720) $
$ [1,1e7] 内 因子个数max = 448 (8648640) $
$ [1,1e8] 内 因子个数max = 768 (73513440) $
$ [1,1e18] 内 因子个数max = 1e5 $
然后直接做就可以了,有了上边的结论就不用担心时间复杂度有问题了。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define Blue_Archive return 0
#define con putchar_unlocked(' ')
#define ent putchar_unlocked('\n')
using namespace std;
constexpr int N = 2e3 + 3;
constexpr int M = 1e5 + 7;
constexpr int INF = 1e18;
constexpr int mod = 1e9 + 7;
constexpr char me[] = "終末なにしてますか?忙しいですか?救ってもらっていいですか?";int n;
int m;
int cnt;
int top;
int sum;
int t[N];
int a[N];
int pri[M];
int tim[15];
int vec[15];bool st[M];vector<int> yz[N];unordered_map<int,int> mp;inline int read()
{int k = 0,f = 1;char c = getchar_unlocked();while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar_unlocked();} while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();return k * f;
}inline void write(int x)
{if(x < 0) putchar_unlocked('-'),x = -x;if(x > 9) write(x / 10);putchar_unlocked(x % 10 + '0');
}inline void init(int up)
{for(int i = 2;i <= up;i ++){if(!st[i]) pri[++ cnt] = i;for(int j = 1;pri[j] * i <= up && j <= cnt;j ++){st[i * pri[j]] = 1;if(i % pri[j] == 0) break;}}
}inline void dfs(int x,int up,int res)
{if(res > n) return;if(x == up){mp[res] ++;return;}for(int i = 0,op = 1;i <= tim[x];i ++){dfs(x + 1,up,res * op);op *= vec[x];}
}inline void work(int x)
{top = 0;for(int i = 1;pri[i] * pri[i] <= x;i ++){if(x % pri[i] == 0){vec[++ top] = pri[i];tim[top] = 0;while(x % pri[i] == 0) x /= pri[i],tim[top] ++;}}if(x > 1) vec[++ top] = x,tim[top] = 1;dfs(1,top + 1,1);
}signed main()
{freopen("div.in","r",stdin);freopen("div.out","w",stdout);// freopen("data.in","r",stdin);freopen("data.out","w",stdout);n = read();m = read();init(M - 7);for(int i = 1;i <= m;i ++){a[i] = read();work(a[i]);} for(auto op : mp){if(op.second){t[op.second] ++;sum ++;}}write(n - sum),ent;for(int i = 1;i <= m;i ++) write(t[i]),ent;Blue_Archive;
}
/*
10 3
4 6 70 : 5 8 9 10
1 : 3 4 6 7
2 : 2
3 : 1
*/
T2: Market
因为看错数据范围导致狂挂 60 tps,难蚌!
首先因为加了时间的限制,所以将超市按营业时间从小到大排序是一定的。
因为考虑到价值的和仅仅为 \(9e4\) 而体积是 \(1e9\) 级别的。(注意!这俩玩意而不是同阶的)
所以很容易想到改变传统背包 DP 的定义。
考虑求在一段时间内的价格为定值的最小体积。对于每次查询先二分其合法的时间,再二分最大的价格即可。
\(dp_{i,j}\) 表示在按时间排序后的第 \(i\) 个超市开门前,价值为 \(j\) 的最小体积。
点击查看代码
#include<bits/stdc++.h>
#define Blue_Archive return 0
#define con putchar_unlocked(' ')
#define ent putchar_unlocked('\n')
using namespace std;
constexpr int N = 3e2 + 3;
constexpr int M = 9e4 + 3;
constexpr int INF = 123456789;
constexpr int mod = 1e9 + 7;
constexpr char me[] = "終末なにしてますか?忙しいですか?救ってもらっていいですか?";int n;
int m;
int VV;
int ans;
int mn[N][M];
int dp[N][M];struct miku
{int c;int v;int t;
}a[N];inline int read()
{int k = 0,f = 1;char c = getchar_unlocked();while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar_unlocked();} while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();return k * f;
}inline void write(int x)
{if(x < 0) putchar_unlocked('-'),x = -x;if(x > 9) write(x / 10);putchar_unlocked(x % 10 + '0');
}inline int find(int val)
{int l = 1,r = n,mid,res = 0;while(l <= r){mid = (l + r) >> 1;if(a[mid].t <= val) l = mid + 1,res = mid;else r = mid - 1;}return res;
}inline int find_val(int pos,int V)
{int l = 1,r = VV,mid,res = 0;while(l <= r){mid = (l + r) >> 1;if(dp[pos][mid] <= V) l = mid + 1,res = mid;else r = mid - 1;}return res;
}signed main()
{freopen("market.in","r",stdin);freopen("market.out","w",stdout);// freopen("data.in","r",stdin);freopen("data.out","w",stdout);n = read();m = read();for(int i = 1;i <= n;i ++){a[i].c = read(); // ti jia[i].v = read(); // jia zhia[i].t = read(); // shi jianVV += a[i].v;} memset(dp,0x3f,sizeof(dp));sort(a + 1,a + n + 1,[](miku a,miku b){return a.t < b.t;});dp[0][0] = 0;for(int i = 1;i <= n;i ++){dp[i][0] = 0;for(int j = VV;j >= a[i].v;j --){dp[i][j] = min(dp[i - 1][j],dp[i - 1][j - a[i].v] + a[i].c);}for(int j = 0;j < a[i].v;j ++) dp[i][j] = min(dp[i][j],dp[i - 1][j]);}for(int i = 1;i <= n;i ++){for(int j = VV;j >= 0;j --){dp[i][j] = min(dp[i][j],dp[i][j + 1]);}}for(int i = 1,t,V,pos;i <= m;i ++){t = read();V = read();pos = find(t);write(find_val(pos,V));ent;}Blue_Archive;
}
T3:连通性
大困难题,场上因为打表抄错了,导致又挂了 35 tps。唉。
很容易可以将问题转化为:对于 \([n - m + 1,n]\) 区间中的点,不存在其相连的不属于该集合的点集的连通块有连边的情况。
首先考虑一下部分分。
对于 \(n == m\) 的情况,我们要考虑到实际上就是把 \([1,n]\) 分成若干个强联通块,答案就是贝尔数的第 \(n\) 项。(这个神秘贝尔数赛后才知道)
对于 \(m == 1\) 的情况,我们只需要考虑一个点。枚举其所有的连边情况,然后对于每种连边需要保证与其相连的连通块中的 \([1,n - m]\) 中的点是互相相连的。
然后我就不会了。。。
赛时考虑到这儿已经挺逼近正解了,只是实在想不到了。。。
然后对着题解狂贺。
考虑到一种 \(dp_{i,j}\),为 $ n == i 并且 m == j$ 时的答案。
考虑枚举所有点的连通块来容斥一下。
然后分别考虑 \([1,i - j]\) 的点所处连通块与 \([i - j + 1,i]\) 的点所处的连通块。
然后疯狂推柿子。
首先对于只有后者,柿子还挺显然的:
\(dp_{i,j} = \sum_{k = 1}^m dp_{i - k,j - i} C(j - 1,k - 1)\)。(就是枚举连通块的大小,在取一下组合数)。
然而考虑加上前者就不好搞了。。。
首先定义一个数组 \(h\)。
定义 \(h_i = 2^{i(i - 1) / 2}\)。
然后在钦定一个数组 \(g\)。表示不连通的个数(容斥一下)(不太明白)
\(g_I = h_i - \sum_{j = 1}^{n - 1} g_j h_{i - j} C(i - 1,j - 1)\)。
之后就能推出来添加了 \([1,i - j]\) 后 \([i - j + 1,i]\) 的点所处连通块的贡献了。
然后就有完整柿子了
\(dp_{i,j} = \sum_{k = 1}^j \sum_{s = 1}^{i - j} dp_{i - k - s,j - k} h_k g_s C(j - 1,k - 1) C(i - j,s) (2^s - 1)^k\)
点击查看代码
#include<bits/stdc++.h>
#define Blue_Archive return 0
#define con putchar_unlocked(' ')
#define ent putchar_unlocked('\n')
using namespace std;
constexpr int N = 1e2 + 1;
constexpr int mod = 1e9 + 7;
constexpr char me[] = "終末なにしてますか?忙しいですか?救ってもらっていいですか?";int T;
int n;
int m;
int g[N];
int h[N];
int fac[N];
int inv[N];
int bel[N];
int dp[N][N];inline int read()
{int k = 0,f = 1;char c = getchar_unlocked();while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar_unlocked();} while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();return k * f;
}inline void write(int x)
{if(x < 0) putchar_unlocked('-'),x = -x;if(x > 9) write(x / 10);putchar_unlocked(x % 10 + '0');
}inline int qpow(int a,int b)
{int res = 1;while(b){if(b & 1) res = (1ll * res * a) % mod;a = (1ll * a * a) % mod;b >>= 1;}return res;
}inline int C(int n,int m)
{if(n < m) return 0;return 1ll * fac[n] * inv[n - m] % mod * inv[m] % mod;
}inline void init()
{bel[0] = fac[0] = g[0] = h[0] = inv[0] = 1;for(int i = 1;i <= 100;i ++){fac[i] = 1ll * fac[i - 1] * i % mod;inv[i] = qpow(fac[i],mod - 2);g[i] = h[i] = qpow(2,(i - 1) * i / 2);bel[i] = C(i - 1,0);for(int j = 1;j < i;j ++){g[i] = (g[i] - 1ll * g[j] * h[i - j] % mod * C(i - 1,j - 1) % mod + mod) % mod;bel[i] = (bel[i] + 1ll * C(i - 1,j) * bel[j] % mod) % mod;}}dp[0][0] = 1;for(int i = 1;i <= 100;i ++){dp[i][0] = h[i];dp[i][i] = bel[i];for(int j = 1;j < i;j ++){for(int k = 1;k <= j;k ++){dp[i][j] = (dp[i][j] + 1ll * dp[i - k][j - k] * C(j - 1,k - 1) % mod) % mod;for(int s = 1;s <= i - j;s ++){dp[i][j] = (dp[i][j] + 1ll * dp[i - k - s][j - k] * C(j - 1,k - 1) % mod * C(i - j,s) % mod * qpow(qpow(2,s) - 1,k) % mod * h[k] % mod * g[s] % mod) % mod;}}}}
}signed main()
{freopen("connect.in","r",stdin);freopen("connect.out","w",stdout);// freopen("data.in","r",stdin);freopen("data.out","w",stdout);T = read();init();while(T --) {n = read();m = read();write(dp[n][m]),ent;}Blue_Archive;
}
T4:树
严格简单于 T3,直接输出 0 有 37 tps...(什么不可以总司令?)
很容易可以将问题转化成求被覆盖的路径条数,与剩余的边的个数。
2 的这个和次幂即为答案。
考虑如何去维护。
发现对于每条路径来说,可以从 \(lca\) 将其分成两条,且需要保证两条路径的方向不同。
所以可以将每条边下放到深度大的节点,然后将每个节点分成两个点。
如将节点 \(i\) 分成 \(i\) 和 \(i + n\)。
对于一条边 \(u - v\)
如果连向下的边,就连接 \(u\) 和 \(v\) 与 \(u + n\) 和 \(v + n\)。
反之则连 \(u + n\) 和 \(v\) 与 \(u\) 和 \(v + n\)。
至于怎么用维护这个,直接上并查集就行。
但是如果我们直接暴力跳每条路径,时间复杂度是 \(O(n ^ 2 log_2{n})\) 的。
不能接受,所以我们再上一棵线段树去维护区间的连边情况,就可以做到 \(O(n log_2^2{n})\)。可以通过此题。
其实真的很好想,只是赛时都在凹 T3 去了,没打 T4 ,大悲。
点击查看代码
#include<bits/stdc++.h>
// #define int long long
// #define ld double
#define lid (id << 1)
#define rid (id << 1 | 1)
#define Blue_Archive return 0
#define con putchar_unlocked(' ')
#define ent putchar_unlocked('\n')
#define add(x,y) to[++ tot] = y,nxt[tot] = h[x],h[x] = tot
using namespace std;
constexpr int N = 3e5 + 3;
constexpr int M = 5e6 + 7;
constexpr int mod = 1e9 + 7;
constexpr char me[] = "終末なにしてますか?忙しいですか?救ってもらっていいですか?";int T;
int n;
int m;
int cnt;
int tot;
int ans;
int tim;
int a[N];
int b[N];
int h[N];
int f[N << 1];
int fa[N];
int to[M];
int rk[N];
int nxt[M];
int son[N];
int siz[N];
int top[N];
int dfn[N];
int dep[N];bool tr[N << 2];inline int read()
{int k = 0,f = 1;char c = getchar_unlocked();while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar_unlocked();} while(c >= '0' && c <= '9') k = (k << 3) + (k << 1) + c - '0',c = getchar_unlocked();return k * f;
}inline void write(int x)
{if(x < 0) putchar_unlocked('-'),x = -x;if(x > 9) write(x / 10);putchar_unlocked(x % 10 + '0');
}inline int qpow(int a,int b)
{int res = 1;while(b){if(b & 1) res = (1ll * res * a) % mod;a = (1ll * a * a) % mod;b >>= 1;}return res;
}inline int find(int x){return f[x] == x ? x : f[x] = find(f[x]);}inline void link(int u,int v)
{u = find(u),v = find(v);if(u != v) f[u] = v;
}inline void meg(int u,int v,int op)
{if(op){link(u,v + n);link(u + n,v);}else {link(u,v);link(u + n,v + n);}
}inline void dfs1(int x,int f)
{fa[x] = f;siz[x] = 1;dep[x] = dep[f] + 1;for(int i = h[x];i;i = nxt[i]){if(to[i] == f) continue;dfs1(to[i],x);siz[x] += siz[to[i]];if(siz[to[i]] > siz[son[x]]) son[x] = to[i];}
}inline void dfs2(int x,int tp)
{top[x] = tp;dfn[x] = ++ tim;rk[tim] = x;if(son[x]) dfs2(son[x],tp);for(int i = h[x];i;i = nxt[i]){if(to[i] == fa[x] || to[i] == son[x]) continue;dfs2(to[i],to[i]);}
}inline int LCA(int x,int y)
{while(top[x] != top[y]){if(dep[top[x]] < dep[top[y]]) swap(x,y);x = fa[top[x]];}return dep[x] > dep[y] ? y : x;
}inline void change(int id,int l,int r,int L,int R)
{if(R < L) return;if(tr[id]) return;if(l == r){meg(fa[rk[l]],rk[l],0);tr[id] = 1;return;}int mid = (l + r) >> 1;if(L <= mid) change(lid,l,mid,L,R);if(R > mid) change(rid,mid + 1,r,L,R);tr[id] = tr[lid] & tr[rid];
}signed main()
{freopen("usmjer.in","r",stdin);freopen("usmjer.out","w",stdout);// freopen("data.in","r",stdin);freopen("data.out","w",stdout);n = read();m = read();for(int i = 1,x,y;i < n;i ++){x = read();y = read();add(x,y);add(y,x);f[i] = i;f[i + n] = i + n;}f[n] = n;f[n + n] = n + n;dfs1(1,0);dfs2(1,1);for(int i = 1,a,b,lca;i <= m;i ++){a = read();b = read();lca = LCA(a,b);if(a != lca && b != lca) meg(a,b,1);while(top[a] != top[lca]){change(1,1,n,dfn[top[a]] + (fa[top[a]] == lca),dfn[a]);a = fa[top[a]];}change(1,1,n,dfn[lca] + 2,dfn[a]);while(top[b] != top[lca]){change(1,1,n,dfn[top[b]] + (fa[top[b]] == lca),dfn[b]);b = fa[top[b]];}change(1,1,n,dfn[lca] + 2,dfn[b]);}for(int i = 2,p,q;i <= n;i ++){p = find(i);q = find(i + n);if(p == q){puts("0");Blue_Archive;}cnt += (i != p) + (i + n != q);}write(qpow(2,n - 1 - (cnt >> 1)));ent;Blue_Archive;
}