A
矩阵树定理可以用于最小生成树计数,最直观的做法就是求个mst,再用矩阵树定理求最小生成树个数,但是n<=1e5,显然不是o(n^3)可以做出来的。
考虑随机数据生成器,固定1e5的边,但是边权在unsigned long long的范围内随机指定,由样例看出,即使是点数很少的情况下,最多也只有一个生成树
于是,我们猜测,只需要做一遍最小生成树,并假定不会有更多的生成树就可以了。
#include#define ull unsigned long long using namespace std; const int maxn = 100050; const ull mod = 1000000007; int f[maxn]; int findf(int x){ return f[x] == x ? x : f[x] = findf(f[x]); } int n,m; ull k1,k2; struct edge{ int u; int v; ull w; friend bool operator < (edge a,edge b){ return a.w < b.w; } }e[maxn]; ull xorS(){ ull k3 = k1,k4 = k2; k1 = k4; k3 ^= k3 << 23; k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26); return k2 + k4; } int main(){ int T; cin>>T; while(T--){ cin>>n>>m>>k1>>k2; for(int i = 1;i <= m;i++){ e[i].u = xorS() % n + 1; e[i].v = xorS() % n + 1; e[i].w = xorS(); } sort(e+1,e+1+m); for(int i = 1;i <= n;i++){ f[i] = i; } ull tot = 0; int uu,vv,cnt = 0; for(int i = 1;i <= m;i++){ uu = e[i].u; vv = e[i].v; uu = findf(uu); vv = findf(vv); if(uu == vv) continue; else{ cnt++; e[i].w %= mod; tot = (tot + e[i].w) % mod; f[uu] = vv; } } if(cnt != n-1) tot = 0; cout< endl; } return 0; }
G
选择k个路径,它们至少有一个交点,首先先把路径经过的点标记一下,对于一个点,它被选中,至少要有k个路径经过它。
这样就有一个需要考虑的地方,如果这k个路径有不止一个交点,就会被重复统计,如何去重?
两条路径的交点,只可能是树上连续的一段,所以多条路径的交点,也只能是树上的一段,对于一个方案,可以只在深度最小的那个点统计一次,而这个点,肯定是某个路径深度最小的点。
遍历每个点,新加进去以这个点为lca的路径,和经过这个点的其他路径联合起来算对方案的贡献。
如何快速计算一个点经过多少路径?可以让深度最深的点+1,走出这条路径或者达到交点(lca)时-1,也就是树上差分。
#include#include #include <string> #include #include #include #include #include #define fo(i, l, r) for (long long i = l; i <= r; i++) #define fd(i, l, r) for (long long i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define ll long long #define ld double using namespace std; const int maxn = 300050; const ll mod = 1e9 + 7; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } int n, m, k; vector<int> g[maxn]; int d[maxn]; int fa[maxn][25]; void dfs(int u, int f, int deep) { d[u] = deep; fa[u][0] = f; int t = 0; while (fa[u][t] && fa[fa[u][t]][t]) { fa[u][t + 1] = fa[fa[u][t]][t]; t++; } int sz = (int)g[u].size() - 1, v; fo(i, 0, sz) { v = g[u][i]; if (v == f) continue; dfs(v, u, deep + 1); } } int lca(int u, int v) { if (d[u] < d[v]) swap(u, v); int delta = d[u] - d[v]; int t = 0; while (delta) { if (delta & 1) u = fa[u][t]; t++; delta >>= 1; } if (u == v) return u; t = 0; while (t >= 0) { if (fa[u][t] != fa[v][t]) { u = fa[u][t]; v = fa[v][t]; t++; } else { t--; } } return fa[u][0]; } int addamt[maxn], cf[maxn], uu[maxn], vv[maxn]; ll fac[maxn], inv[maxn]; ll ans; ll C(ll n, ll m) { return fac[n] * inv[m] % mod * inv[n - m] % mod; } void dfs2(int u) { int v, sz = (int)g[u].size() - 1; fo(i, 0, sz) { v = g[u][i]; if (v == fa[u][0]) continue; dfs2(v); cf[u] += cf[v]; } } int main() { //freopen("data.in","r",stdin); fac[0] = fac[1] = 1; fo(i, 2, maxn - 1) { fac[i] = (fac[i - 1] * i) % mod; } inv[0] = inv[1] = 1; fo(i, 2, maxn - 1) { inv[i] = (mod - (mod / i)) * inv[mod % i] % mod; } fo(i, 2, maxn - 1) { inv[i] = (inv[i] * inv[i - 1]) % mod; } int T = read(); while (T--) { memset(fa, 0, sizeof(fa)); memset(d, 0, sizeof(d)); ans = 0; n = read(); m = read(); k = read(); fo(i, 1, n) g[i].clear(); int u, v; fo(i, 1, n - 1) { u = read(); v = read(); g[u].push_back(v); g[v].push_back(u); } fo(i, 1, m) { uu[i] = read(); vv[i] = read(); } dfs(1, 0, 1); fo(i, 1, n) cf[i] = addamt[i] = 0; int zz, cd; fo(i, 1, m) { zz = lca(uu[i], vv[i]); addamt[zz]++; cf[uu[i]]++; cf[vv[i]]++; cf[zz]--; cf[fa[zz][0]]--; } dfs2(1); fo(i, 1, n) { cf[i] -= addamt[i]; fo(j, 1, addamt[i]) { if (j > k) break; if (cf[i] >= k - j){ ans = (ans + C(cf[i], k - j) * C(addamt[i], j) % mod) % mod; } } } printf("%lld\n", ans); } return 0; }
H
如果区间的数量不大于颜色数,那么每个区间染上不同的颜色就可以了。
如果有断层,尽量染上缺少的颜色,如果区间比较多,如何确保染到一个最优的颜色?
某些颜色在大于某个位置就没有了,如果染上那个最早失效的颜色,就可能会使答案增大。
对于每个颜色,维护一个它消失的位置,每次取出那个消失位置最靠前的染色。
这个题spj有毛病,行尾有空格算你wa,是真的无语。
#include#include #include <string> #include #include #include #include #include #define fo(i, l, r) for (long long i = l; i <= r; i++) #define fd(i, l, r) for (long long i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define ll long long #define ld double using namespace std; const int maxn = 200050; const ll mod = 1e9 + 7; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } struct dat{ int p; ll v; friend bool operator < (dat a,dat b){ return a.v > b.v; } }now,nxt,col; priority_queue q,ys; ll n,k,ans[maxn],op[maxn],ed[maxn],cnt[maxn]; ll tot; int main() { int T=read(); while(T--){ tot=0; while(!q.empty())q.pop(); while(!ys.empty())ys.pop(); n=read();k=read(); if(k>n)k=n; fo(i,1,n)ans[i]=0; fo(i,1,k)cnt[i]=0; int nowp=0,nowk=0; fo(i,1,n){ now.p=i; now.v=read(); q.push(now); op[i] = now.v; now.v=read(); q.push(now); ed[i] = now.v; } fo(i,1,k){ now.p=i; now.v=-1; ys.push(now); } while(!q.empty()){ now=q.top(); q.pop(); if(ans[now.p]){ cnt[ans[now.p]]--; if(cnt[ans[now.p]]==0) nowk--; }else{ col=ys.top(); ys.pop(); ans[now.p] = col.p; cnt[col.p]++; if(cnt[col.p]==1) nowk++; col.v = max(col.v,(ll)ed[now.p]); ys.push(col); } nowp = now.v; if(!q.empty()){ nxt=q.top(); if(nxt.v == now.v) continue; else{ if(nowk >= k) tot += nxt.v - now.v; } } } printf("%lld\n",tot); printf("%lld",ans[1]); fo(i,2,n) printf(" %lld",ans[i]); putchar('\n'); } return 0; }
M
首先预处理每个灯照亮的边,它是一段连续的区间。
能不能照亮某个边,需要 点与边的两个端点的连线不经过凸包内部,可以利用叉积快速判断,逆时针方向下只要点在边的右边就可以。
处理之后,要做一个线段覆盖,由于是环,需要做n次
#include#include #include <string> #include #include #include #include #include #include <set> #include #include #include
I
要构造一个最长上升子序列长度至少为n-1的序列,可以让一个数移动到另一个数后边,这样就有o(n^2)个
每个序列,经过排序之后,若符合条件,那么一定在这些目标序列之内。
可以倒着枚举哪些排序器起到了作用。
#include#include #include <string> #include #include #include #include #include #define fo(i, l, r) for (long long i = l; i <= r; i++) #define fd(i, l, r) for (long long i = r; i >= l; i--) #define mem(x) memset(x, 0, sizeof(x)) #define ll long long #define ld double using namespace std; const int maxn = 200050; const ll mod = 1e9 + 7; ll read() { ll x = 0, f = 1; char ch = getchar(); while (!(ch >= '0' && ch <= '9')) { if (ch == '-') f = -1; ch = getchar(); }; while (ch >= '0' && ch <= '9') { x = x * 10 + (ch - '0'); ch = getchar(); }; return x * f; } struct dat{ int v[55]; }now,nxt,dats[maxn]; int n,m,cnt; int a[50],b[50]; ll q,ans; void dfs(int u,int d){ if(d > m){ ans++; return; } int aa=a[m-d+1],bb=b[m-d+1]; if(dats[u].v[aa] > dats[u].v[bb]) return; dfs(u,d+1); swap(dats[u].v[aa],dats[u].v[bb]); dfs(u,d+1); swap(dats[u].v[aa],dats[u].v[bb]); } int main() { int T=read(); while(T--){ n=read(); m=read(); q=read(); cnt=ans=0; fo(i,1,m){ a[i]=read(); b[i]=read(); } fo(i,1,n){ dats[0].v[i]=i; } fo(i,1,n){ fo(j,0,n){ now = dats[0]; if(i==j||i==j+1||i==j-1)continue; if(j<i){ int tmp = i; fd(k,j+1,i-1){ swap(now.v[k],now.v[k+1]); } now.v[j+1]=tmp; }else{ int tmp = i; fo(k,i+1,j){ swap(now.v[k],now.v[k-1]); } now.v[j]=tmp; } cnt++; dats[cnt]=now; } } cnt++; dats[cnt] = dats[0]; fo(i,1,cnt){ dfs(i,1); } ans %= mod; printf("%lld\n",ans); } return 0; }