A
矩阵树定理可以用于最小生成树计数,最直观的做法就是求个mst,再用矩阵树定理求最小生成树个数,但是n<=1e5,显然不是o(n^3)可以做出来的。
考虑随机数据生成器,固定1e5的边,但是边权在unsigned long long的范围内随机指定,由样例看出,即使是点数很少的情况下,最多也只有一个生成树
于是,我们猜测,只需要做一遍最小生成树,并假定不会有更多的生成树就可以了。
#include<bits/stdc++.h>
#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<<tot<<endl;
}
return 0;
}
G
选择k个路径,它们至少有一个交点,首先先把路径经过的点标记一下,对于一个点,它被选中,至少要有k个路径经过它。
这样就有一个需要考虑的地方,如果这k个路径有不止一个交点,就会被重复统计,如何去重?
两条路径的交点,只可能是树上连续的一段,所以多条路径的交点,也只能是树上的一段,对于一个方案,可以只在深度最小的那个点统计一次,而这个点,肯定是某个路径深度最小的点。
遍历每个点,新加进去以这个点为lca的路径,和经过这个点的其他路径联合起来算对方案的贡献。
如何快速计算一个点经过多少路径?可以让深度最深的点+1,走出这条路径或者达到交点(lca)时-1,也就是树上差分。
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#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 <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#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<dat> 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;
}