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


    2020-09-03 考试题解

    Hobson's Trains

    题目传送门

    题目大意

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

    (nle 5 imes 10^5)

    思路

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

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

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

    ( exttt{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 ('
    ');
    	return 0;
    }
    

    拯救小矮人

    题目传送门

    题目大意

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

    问答案的最大可能值。(nle 2 imes 10^5)

    思路

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

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

    时间复杂度 (Theta(nlog n))

    ( exttt{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 ('
    ');
    	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)(tle 100,n,m,kle 5 imes 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) 同阶)。

    ( exttt{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 ('
    ');
    

    2020-09-04 考试题解

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

    express

    题目大意

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

    (nle 300,mle 4000,kle 300)

    思路

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

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

    ( exttt{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 ('
    ');
    

    Graph

    题目大意

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

    (nle 10^5)

    思路

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

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

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

    时间复杂度 (Theta(nlog^2 n))

    ( exttt{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 ('
    ');
    	return 0;
    }
    
  • 相关阅读:
    电子海图开发一百篇第五十五篇-电子江图传输规范 数据模型
    天气可视化,海浪,温度图层的绘制,温度热力图的可视化
    全球潮汐数据API使用方法,潮汐数据查询
    海洋气象数据可视化,以流场的方式显示风场图,海洋气象API使用
    g++ 编译module失败
    编译gcc error-*** LIBRARY_PATH shouldn‘t contain the current directory when *** building gcc.
    windows两种自启动的区别
    windows多线程加锁
    windows server 2012不显示此电脑
    pthread_cond_wait
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13618178.html
Copyright © 2020-2023  润新知