2021-06-18 集训题解
今天爆傻了
T1 种蘑菇
Description
有一个 \(n\) 个点的树,问 \(\sum \gcd\{S\}^{|S|}\),其中 \(S\) 是树上的一个连通块。
\(n\le 10^5\),答案对 \(10^9+7\) 取模。
Solution
很水,可惜我是zz。。。
可以想到,如果我们设 \(f_{k,i}\) 表示 \(i|\gcd\{S\}\) 的 \(S\) 的 \(k^{|S|}\) 之和,那么答案就是:
\(f_{k,i}\) 可以直接树形dp,在 \(k,i\) 确定时可以列出 \(f_u\to f_u\times (f_v+1),v\in Son_u\) 的转移式。
复杂度 \(\Theta(n\ln^2n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define MAXN 200005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
int n;
vector <int> G[MAXN];
void link (int u,int v){
G[u].push_back (v),
G[v].push_back (u);
}
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
return res;
}
int inv (int x){return qkpow (x,mod - 2);}
void Add (int &a,int b){a = add (a,b);}
int gcd (int a,int b){return !b ? a : gcd (b,a % b);}
int lcm (int a,int b){return a / gcd (a,b) * b;}
bool vis[MAXN];
int tot,mu[MAXN],prime[MAXN];
void Euler (){
mu[1] = 1;
for (Int i = 2;i <= n;++ i){
if (!vis[i]) prime[++ tot] = i,mu[i] = -1;
for (Int j = 1;j <= tot && i * prime[j] <= n;++ j){
vis[i * prime[j]] = 1;
if (i % prime[j]) mu[i * prime[j]] = -mu[i];
else break;
}
}
}
int ind,q[MAXN],dfn[MAXN];
void init (int u,int fa){
dfn[u] = ++ ind;
for (Int v : G[u]) if (v ^ fa) init (v,u);
}
bool visit[MAXN];
int K,S,res,f[MAXN];
void dfs (int u,int fa){
f[u] = K,visit[u] = 1;
for (Int v : G[u]) if (v != fa && v % S == 0) dfs (v,u),f[u] = mul (f[u],f[v] + 1);
Add (res,f[u]);
}
int getit (){
int len = n / S;res = 0;
for (Int i = 1;i <= len;++ i) q[i] = i * S;
sort (q + 1,q + len + 1,[](int x,int y){return dfn[x] < dfn[y];});
for (Int i = 1;i <= len;++ i) if (!visit[q[i]]) dfs (q[i],0);
for (Int i = 1;i <= len;++ i) visit[q[i]] = 0;
return res;
}
signed main(){
freopen("mushroom.in", "r", stdin);
freopen("mushroom.out", "w", stdout);
read (n),Euler ();
for (Int i = 2,u,v;i <= n;++ i) read (u,v),link (u,v);
init (1,0);int ans = 0;
for (K = 1;K <= n;++ K)
for (Int i = 1;i <= n / K;++ i)
if (mu[i]){
S = i * K;
int tmp = getit ();
if (mu[i] > 0) Add (ans,tmp);
else ans = dec (ans,tmp);
}
write (ans),putchar ('\n');
return 0;
}
T2 轮回
Description
并不是很想搬。
Solution
可以想到设 \(f_{i,j}\) 表示到了第 \(i\) 个节点,离中心线距离为 \(j\) 时还要走的期望长度,可以列出转移式:
边界条件即是 \(f_{i,k+1}=0\)。
然后我们看出,这个转移式可以写成矩阵乘法,即 \(F_i=M_i\times F_{i+1}\),那么我们就可以得到 \(F_x=M_x\times M_{x+1}\times ...\times M_n\times M_1\times ...\times M_{x-1}\times F_x\)。
那么我们就可以高斯消元。矩阵乘法部分可以线段树优化。复杂度 \(\Theta(nk^3\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define inv2 500000004
#define mod 1000000007
#define MAXN 100005
#define MAXM 8
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
int n,K,q,a[MAXN],b[MAXN],p[MAXN];
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
return res;
}
int inv (int x){return qkpow (x,mod - 2);}
void Add (int &a,int b){a = add (a,b);}
struct Matrix{
int val[MAXM][MAXM];
Matrix(){memset (val,0,sizeof (val));}
int * operator [](int key){return val[key];}
Matrix operator * (const Matrix &p)const{
Matrix New;
for (Int i = 0;i <= K + 1;++ i)
for (Int j = 0;j <= K + 1;++ j)
for (Int k = 0;k <= K + 1;++ k)
Add (New[i][k],mul (val[i][j],p.val[j][k]));
return New;
}
void putout (){
cout << " --------------- " << endl;
for (Int i = 0;i <= K + 1;++ i){
for (Int j = 0;j <= K + 1;++ j)
cout << val[i][j] << " ";
cout << endl;
}
cout << " ------------ " << endl;
}
};
struct Segment{
Matrix Sum[MAXN << 2];
void buildit (int x,int l){
Sum[x][0][0] = p[l],Sum[x][0][1] = dec (1,p[l]);
for (Int i = 1;i <= K;++ i){
Sum[x][i][i] = p[l],Sum[x][i][i - 1] = mul (inv2,dec (1,p[l]));
if (i != K) Sum[x][i][i + 1] = Sum[x][i][i - 1];
}
for (Int i = 0;i <= K + 1;++ i) Sum[x][i][K + 1] = 1;
}
void pushup (int x){Sum[x] = Sum[x << 1] * Sum[x << 1 | 1];}
void build (int x,int l,int r){
if (l == r) return buildit (x,l);
int mid = (l + r) >> 1;
build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r),pushup (x);
}
Matrix query (int x,int l,int r,int ql,int qr){
if (ql > qr){
Matrix res;
for (Int i = 0;i <= K + 1;++ i) res[i][i] = 1;
return res;
}
if (l >= ql && r <= qr) return Sum[x];
int mid = (l + r) >> 1;Matrix res;
for (Int i = 0;i <= K + 1;++ i) res[i][i] = 1;
if (ql <= mid) res = res * query (x << 1,l,mid,ql,qr);
if (qr > mid) res = res * query (x << 1 | 1,mid + 1,r,ql,qr);
return res;
}
void change (int x,int l,int r,int pos){
if (l == r) return buildit (x,l);
int mid = (l + r) >> 1;
if (pos <= mid) change (x << 1,l,mid,pos);
else change (x << 1 | 1,mid + 1,r,pos);
pushup (x);
}
}Tree;
int A[MAXM][MAXM],tmp[MAXM];
int Gaussit (){
int up = K;
for (Int i = 0;i <= up;++ i){
int pos = i;
for (Int j = i;j <= up;++ j) if (A[j][i] > A[pos][i]){pos = j;break;}
if (pos ^ i) swap (A[i],A[pos]);int iv = inv (A[i][i]);
for (Int j = i + 1;j <= up;++ j){
int det = mul (A[j][i],iv);
for (Int k = i;k <= up + 1;++ k) A[j][k] = dec (A[j][k],mul (det,A[i][k]));
}
}
for (Int i = up;i >= 0;-- i){
int fuc = A[i][up + 1];
for (Int j = up;j > i;-- j) fuc = dec (fuc,mul (A[i][j],tmp[j]));
tmp[i] = mul (fuc,inv (A[i][i]));
}
return tmp[0];
}
int Solve (int x){
Matrix res = Tree.query (1,1,n,x,n) * Tree.query (1,1,n,1,x - 1);
// res.putout();
for (Int i = 0;i <= K;++ i){
for (Int j = 0;j <= K;++ j) A[i][j] = res[i][j];
A[i][i] --,A[i][K + 1] = mod - res[i][K + 1];
}
return Gaussit ();
}
signed main(){
freopen("samsara.in", "r", stdin);
freopen("samsara.out", "w", stdout);
read (n,K,q);
for (Int i = 1;i <= n;++ i) read (a[i],b[i]),p[i] = mul (a[i],inv (b[i]));
Tree.build (1,1,n);
while (q --> 0){
int kase,i;read (kase,i);
if (kase == 1) write (Solve (i)),putchar ('\n');
else{
int a,b;read (a,b);
p[i] = mul (a,inv (b)),Tree.change (1,1,n,i);
}
}
return 0;
}
T3 洞
Description
还是懒得搬。
Solution
假设一个球距离两边最近的距离为 \(x,y\),那么我们可以把它看作一个点 \((x,y)\)。
可以想到的是,如果对于点 \((x2,y2)\) 存在点 \((x1,y1)\),是的 \((x1,y1)\) 在 \((x2,y2)\) 左上方,那么如果 \((x1,y1)\) 是掉进右侧,则 \(x2,y2\) 也是掉进右侧。
发现当且仅当满足该规则时存在解。所以我们可以统计不同的严格上升子序列,即是答案。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define MAXN 100005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int b){
int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
return res;
}
int inv (int x){return qkpow (x,mod - 2);}
void Add (int &a,int b){a = add (a,b);}
#define pii pair<int,int>
pii f[MAXN];
int n,m,cnt,uni,s[MAXN],xh[MAXN],yh[MAXN],tmp[MAXN];
int val[MAXN];
int lowbit(int x){return x & (-x);}
void modify (int x,int v){for (Int i = x;i <= n;i += lowbit (i)) Add (val[i],v);}
int query (int x){int res = 0;for (Int i = x;i;i -= lowbit (i)) Add (res,val[i]);return res;}
bool cmp (pii A,pii B){
return A.first != B.first ? A.first < B.first : A.second > B.second;
}
signed main(){
freopen ("hole.in","r",stdin);
freopen ("hole.out","w",stdout);
read (n,m);
for (Int i = 1;i <= n;++ i) read (xh[i]);
for (Int i = 1;i <= m;++ i) read (yh[i]);
for (Int i = 1;i <= n;++ i)
if (xh[i] > yh[1] && xh[i] < yh[m]){
int pos = lower_bound (yh + 1,yh + m + 1,xh[i]) - yh;
if (yh[pos] == xh[i]) continue;
int x = xh[i] - yh[pos - 1],y = yh[pos] - xh[i];
f[++ cnt] = make_pair (x,y);
}
sort (f + 1,f + cnt + 1,cmp),cnt = unique(f + 1,f + cnt + 1) - f - 1;
for (Int i = 1;i <= cnt;++ i) s[i] = f[i].second,tmp[i] = s[i];
sort (tmp + 1,tmp + cnt + 1),uni = unique (tmp + 1,tmp + cnt + 1) - tmp - 1;
for (Int i = 1;i <= cnt;++ i) s[i] = lower_bound (tmp + 1,tmp + uni + 1,s[i]) - tmp;
int ans = 1;
for (Int i = 1;i <= cnt;++ i){
int tmp = query (s[i] - 1) + 1;
Add (ans,tmp),modify (s[i],tmp);
}
write (ans),putchar ('\n');
return 0;
}