Live2D

2020-09-03/04 考试题目题解

2020-09-03 考试题解

Hobson's Trains

题目传送门

题目大意

给出一个图,保证每个点有且仅有一个出边,对于每个点把它走 \(k\) 步这条路径上的所有的点答案加 1 ,问最后每个点的答案。

\(n\le 5\times 10^5\)

思路

考试的时候 sb 了,没想出来怎么做。

首先你可以发现这其实是个基环树森林,对于不在环上的点,它答案其实就是子树内深度与它相差不超过 \(k\) 的点的数量。考虑在环上的点,显然它们还要考虑环上其他点对该点产生的贡献。

具体实现的话我们可以断开一条边使得变成森林,然后求点的数量可以用树状数组解决,然后如果每次都需要清空的话时间复杂度就会降到 \(\Theta(n^2)\) ,所以我们可以只考虑增长量。时间复杂度 \(\Theta(n\log n)\)

\(\texttt{Code}\)

vector <int> E[MAXN];
int n,k,cutu,cutv,f[MAXN],sum[MAXN],vis[MAXN],lim[MAXN],ans[MAXN];

int lowbit (int x){return x & (-x);}
void modify (int x){for (Int i = x;i <= n;i += lowbit (i)) sum[i] ++;}
int query (int x){int res = 0;for (Int i = min (n,x);i;i -= lowbit (i)) res += sum[i];return res;}

void dfs (int u,int dep){
	vis[u] = 1,ans[u] -= query (dep + k) - query (lim[u]),modify (dep);
	for (int v : E[u]) if (u != cutu || v != cutv) dfs (v,dep + 1);
	ans[u] += query (dep + k) - query (lim[u]);
}

signed main(){
	read (n,k);
	for (Int i = 1;i <= n;++ i) read (f[i]),E[f[i]].push_back (i);
	for (Int i = 1;i <= n;++ i){
		if (vis[i]) continue;
		int u = i,v;
		while (!vis[u]) vis[u] = 1,u = f[u];	
		v = f[u],cutu = v,cutv = u;
		for (Int len = k;v != u;v = f[v],len --) lim[v] = max (0,len);
		for (v = f[u];v != u;v = f[v]) ans[v] -= query (lim[v]);
		dfs (u,1);
		for (v = f[u];v != u;v = f[v]) ans[v] += query (lim[v]); 
	}
	for (Int i = 1;i <= n;++ i) write (ans[i]),putchar ('\n');
	return 0;
}

拯救小矮人

题目传送门

题目大意

一开始有 \(n\) 个点,每个点都两个权值 \(a,b\) ,如果存在集合 \(s\) 使得对于 \(k\) 存在 \(a_i\{i\in s\}+b_k\ge h\) ,那么我们就可以把答案加 1,但是后面的集合就不能包含该点。

问答案的最大可能值。\(n\le 2\times 10^5\)

思路

考试的时候像个弱智一样,写了一个伪贪心,然后写了一个平衡树,结果只把后面比较水的数据骗过去了,还没有乱搞得的分多。。。

正解其实是个反悔贪心,我们按照 \(a+b\) 排序,然后你发现如果我们把满足的点存下来,每次如果当前点可以那么,那就直接加进去,否则就把最强的拉下来,把这个救上去。

时间复杂度 \(\Theta(n\log n)\)

\(\texttt{Code}\)

int n,H,ans;ll suma;

priority_queue <int> q;

struct node{
	int a,b;
	bool operator < (const node &p)const{return a + b < p.a + p.b;}
}peo[MAXN];

signed main(){
	read (n);
	for (Int i = 1;i <= n;++ i) read (peo[i].a,peo[i].b),suma += peo[i].a;
	sort (peo + 1,peo + n + 1),read (H);
	for (Int i = 1;i <= n;++ i){
		q.push (peo[i].a);
		if (suma + peo[i].b >= H) ans ++;
		else suma += q.top(),q.pop ();
		suma -= peo[i].a;
	}
	write (ans),putchar ('\n');
	return 0;
}

Easy

题目传送门

题目大意

定义 \(\{a\}\) 合法,当且仅当 \(\sum_{i=1}^{k}a_i=n\),定义 \(\{b\}\) 合法,当且仅当 \(\sum_{i=1}^{k}b_i=m\),定义 \(\{a,b\}\) 的权值为 \(\prod_{i=1}^{k}\min(a_i,b_i)\),问所有合法 \(\{a,b\}\) 的权值之和。

\(t\) 次查询,每次给出 \(n,m,k\)\(t\le 100,n,m,k\le 5\times 10^5\)

思路

考试的时候随便推了一下,骗了 \(60\) 分就没有想了,主要是因为没想到二元生成函数,想到了之后就很简单了,直接推式子就可以了。

你发现,其实答案就是 :

\[(\sum_{a,b}\min(a,b)x^ay^b)^k[x^ny^m] \]

证明显然。

然后你把上面那个东西写出来,你发现其实就是:

\[1xy+1xy^2+1xy^3+...\\1x^2y+2x^2y^3+2x^2y^4+...\\1x^3y+2x^3y^2+3x^3y^3+3x^3y^4+... \]

然后你用等差比公式推一下发现其实就等价于:

\[\frac{xy}{(1-x)(1-y)(1-xy)} \]

然后求这个东西的 \([x^ny^m]\) 可以直接用隔板法,具体来说就是枚举 \(xy\) 的次数。

时间复杂度 \(\Theta(tn)\) (假设 \(n,m\) 同阶)。

\(\texttt{Code}\)

read (N,M,K);
int ans = 0;
for (Int i = 0;i <= N - K && i <= M - K;++ i)
      ans = add (ans,mul (binom (K + i - 1,i),mul (binom (N - i - 1,N - K - i),binom (M - i - 1,M - K - i))));
write (ans),putchar ('\n');

2020-09-04 考试题解

尝试了头铁打法然后爆炸了。。。

express

题目大意

给出一个 \(n\) 个点,\(m\) 条带权边的图,需要依次访问 \(2k\) 个点,你有两个传送门,传送门放置时必须保证为当前位置,传送门之间可以瞬间传送,问访问完所有点的最小路径长度。

\(n\le 300,m\le 4000,k\le 300\)

思路

考试的时候猜到了一个显然的结论结果没想到用 dp ,然后。。。

首先有一个显然的结论,就是说有一个传送门肯定就在我的脚下,然后我们可以设 \(dp[i][j]\) 表示访问了前 \(i\) 个点,另外一个传送门在 \(j\) 的最小路径长度。显然,\(dp[0][1]=0\)。转移式的话我们可以想到肯定只会在 \(dp[i-1][k]\) 之间转移,其中 \(k\) 是上一次的传送门的位置,然后你枚举一下情况就可以得到转移式了,具体见代码。

\(\texttt{Code}\)

dp[0][1] = 0,a[0] = 1;
for (Int i = 1;i <= k * 2;++ i){
      for (Int j = 1;j <= n;++ j){
	for (Int K = 1;K <= n;++ K)
	      dp[i][j] = min (dp[i][j],dp[i - 1][K] + dist[a[i - 1]][j] + dist[K][a[i]]),
	      dp[i][j] = min (dp[i][j],dp[i - 1][K] + dist[a[i - 1]][j] + dist[j][a[i]]),
	      dp[i][j] = min (dp[i][j],dp[i - 1][K] + dist[a[i - 1]][a[i]] + dist[K][j]),
	      dp[i][j] = min (dp[i][j],dp[i - 1][K] + dist[K][j] + dist[j][a[i]]);		
	}
}
int ans = dp[k * 2][1];
for (Int i = 1;i <= n;++ i) ans = min (ans,dp[k * 2][i]);
write (ans),putchar ('\n');

Graph

题目大意

有一个 \(n\) 个点的边带权的树,你每次可以加边或者删边,但需要保证任意时刻存在的环上的权值异或之值为 \(0\)。问最后 \(n-1\) 条边的最小边权之和。

\(n\le 10^5\)

思路

被wxk和llsw骗了,实际上这道题目没有很难(也有可能是因为自己没做)

我们可以发现如果两个点之间要加边,那么,边权一定是固定的,因为如果构成的环上的不管怎么变边权都是相同的(异或值为 \(0\)),可以想到如果定义 \(p[u]\) 表示 \(1\to u\) 的路径异或值,那么 \(u,v\) 之间的边权就是 \(p[u]\otimes p[v]\)。于是我们就可以对 \(n^2\) 条边做最小生成树。

但是这样显然很慢。我们可以想到,如果从高到低位考虑,那么对于当前位而言,我们应该尽量边相邻两点该位分别为 \(0,1\) 的边的数量最少,那显然就只连 \(1\) 条边,而且这里很方便的是不需要考虑不连通的问题(有 \(n^2\) 条边)。那我们可以直接分治,每次把该位为 \(0\) 的分治构成一棵树,该位为 \(1\) 的构成一棵树,然后两棵树之间连一条边权最小的边。于是问题就是如何找到边权最小的边,然后你发现这个东西直接用 01trie 树解决就好了。

时间复杂度 \(\Theta(n\log^2 n)\)

\(\texttt{Code}\)

ll ans;
int n,tot,p[MAXN],rt[MAXN],cnt[MAXN * 31],son[MAXN * 31][2];

void ins (int a,int b,int t,int x)
{
	if (t < 0) return ;
	int i = x >> t & 1;
	son[a][i] = ++ tot;
	son[a][i ^ 1] = son[b][i ^ 1];
	cnt[son[a][i]] = cnt[son[b][i]] + 1;
	ins (son[a][i],son[b][i],t - 1,x);
}

int query (int a,int b,int t,int x)
{
	if (t < 0) return 0;
	int i = x >> t & 1;
	if (cnt[son[b][i]] > cnt[son[a][i]]) return query (son[a][i],son[b][i],t - 1,x);
	else return (1 << t) + query (son[a][i ^ 1],son[b][i ^ 1],t - 1,x);
}

int toop = 1,to[MAXN << 1],wei[MAXN << 1],nxt[MAXN << 1],head[MAXN];

void Add_Edge (int u,int v,int w){
	to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
	to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
}

void dfs (int u,int fa){
	for (Int i = head[u];i;i = nxt[i]){
		int v = to[i],w = wei[i];
		if (v == fa) continue;
		p[v] = p[u] ^ w,dfs (v,u);
	}
}

void dfs (int t,int l,int r){
	if (l >= r || t < 0) return ;
	int pos = r + 1,tmp = INF;
	for (Int i = l;i <= r;++ i) if (p[i] >> t & 1){pos = i;break;}
	dfs (t - 1,l,pos - 1),dfs (t - 1,pos,r);
	if (pos - 1 >= l && pos <= r){
		for (Int i = pos;i <= r;++ i) tmp = min (tmp,query (rt[l - 1],rt[pos - 1],29,p[i]));
		ans += tmp,tot = 0;
	}
}

signed main(){
	read (n);
	for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),u ++,v ++,Add_Edge (u,v,w);
	dfs (1,0),sort (p + 1,p + n + 1);
	for (Int i = 1;i <= n;++ i) ins (rt[i] = ++ tot,rt[i - 1],29,p[i]);dfs (29,1,n);
	write (ans),putchar ('\n');
	return 0;
}
posted @ 2020-09-05 14:47  Dark_Romance  阅读(161)  评论(0编辑  收藏  举报