Live2D

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|}\) 之和,那么答案就是:

\[\sum_{k=1}^{n} \sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\mu(i)\times f_{k,i\times k} \]

\(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\) 时还要走的期望长度,可以列出转移式:

\[\text{when j}\not= 0:f_{i,j}=1+p_i\times f_{i+1,j}+\frac{1-p_i}{2}\times f_{i+1,j-1}+\frac{1-p_i}{2}\times f_{i+1,j+1} \]

\[\text{when} j=0:f_{i,j}=1+p_i\times f_{i+1,j}+(1-p_i)\times f_{i+1,j-1} \]

边界条件即是 \(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;
}
posted @ 2021-06-18 16:49  Dark_Romance  阅读(32)  评论(0编辑  收藏  举报