zoukankan      html  css  js  c++  java
  • 2020-11-16 考试题解

    考得还行吧,不过这次区分度不是很大。

    接力比赛

    题目传送门

    题目大意

    给出 (n+m) 个人,每个人有两个权值 (v,w),从 (n,m) 个人分别选出若干人使得两队 (sum v) 相同的情况下,(sum w) 最大。

    (nle 1000,vle 1000)

    思路

    不难看出这个题目没有什么特别严谨的做法,直接随机化乱搞就好了,具体来说就是限定任意时刻两队 (v) 的差值不大于某个值,然后做 01 背包即可。

    树上竞技

    题目传送门

    题目大意

    有一棵 (n) 个点的树,在上面选取 (m) 个点作为关键点。找树上的一个点,使得所有关键点到该点的距离之和最小。现在问总共 (inom{n}{m}) 种情况的最小距离和。

    (nle 10^6),答案对 (10^9+7) 取模。

    思路

    不难看出可以对每一条边计算贡献,对于连接的大小为 (v) 的子树,贡献就是:

    [sum_{i=1}^{m-1} min(i,m-i) imes inom{v}{i} imes inom{n-v}{m-i} ]

    然后拆开就是:

    [[mmod 2=0]m/2 imes inom{v}{m/2} imes inom{n-v}{m/2}+sum_{i=1}^{(m-1)/2}i imes (inom{v}{m-i} imes inom{n-v}{i}+inom{v}{i} imes inom{n-v}{m-i}) ]

    你发现后面那个式子比较对称,于是我们只需要考虑后面一部分。

    (p=(m-1)/2)

    [sum_{i=1}^{p}i imes inom{v}{i} imes inom{n-v}{m-i} ]

    [=vsum_{i=1}^{p}inom{v-1}{i-1} imes inom{n-v}{m-i} ]

    考虑其组合意义,发现就是 (n-1) 个球放到 (m-1) 个盒子里,每个盒子最多只能放一个求,满足前面 (v-1) 个盒子球的个数不超过 (p-1) 的方案数。

    你发现这个玩意可以递推,设 (h(s)) 表示 (v=s) 的答案,那么 (h(s))(h(s-1)) 减少的就是 (s-2) 前面有 (p-1) 个,且 (s-1) 放了,所以可以得到递推式:

    [h(s)=h(s-1)-inom{s-2}{p-1} imes inom{n-s}{m-p-1} ]

    然后预处理一下就可以做到 (Theta(n)) 了。

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define mod 1000000007
    #define MAXN 1000005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    vector <int> G[MAXN];
    int n,m,p,ans,H[MAXN],siz[MAXN],fac[MAXN],ifac[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 binom (int a,int b){return a >= b ? mul (fac[a],mul (ifac[b],ifac[a - b])) : 0;}
    
    void dfs (int u){
    	siz[u] = 1;
    	for (Int i = 0;i < G[u].size();++ i){
    		int v = G[u][i];
    		dfs (v),siz[u] += siz[v];
    	}
    	for (Int k = 0;k < G[u].size();++ k){
    		int v = G[u][k];
    		ans = add (ans,add (H[siz[v]],H[n - siz[v]]));
    		if (m % 2 == 0) ans = add (ans,mul (m / 2,mul (binom (siz[v],m / 2),binom (n - siz[v],m / 2))));
    	}
    }
    
    signed main(){
    	int T;read (T);
    	while (T --> 0){
    		read (n),read (m),p = (m - 1) / 2;
    		for (Int i = 1;i <= n;++ i) G[i].clear();ans = 0;
    		for (Int i = 2,fa;i <= n;++ i) read (fa),G[fa].push_back (i);
    		fac[0] = 1;for (Int i = 1;i <= n;++ i) fac[i] = mul (fac[i - 1],i);
    		ifac[n] = qkpow (fac[n],mod - 2);for (Int i = n;i;-- i) ifac[i - 1] = mul (ifac[i],i);
    		H[1] = p >= 1 ? binom (n - 1,m - 1) : 0;for (Int i = 2;i <= n;++ i) H[i] = dec (H[i - 1],mul (binom (i - 2,p - 1),binom (n - i,m - p - 1)));
    		for (Int i = 1;i <= n;++ i) H[i] = mul (H[i],i);
    		dfs (1),write (ans),putchar ('
    ');
    	}
    	return 0;
    }
    

    虚构推理

    题目传送门

    题目大意

    给出 (n) 个时钟,求出一个时刻使得该时刻分针分针角度最大值最小。

    (nle 5 imes 10^4)

    思路

    直接二分,判断的话直接区间取交即可,判断时钟的时刻是否包含分针的时刻即可。

    问题就是如何取交,实际上你发现每一个区间长度都是固定的,所以排个序就好了。

    记忆碎片

    题目传送门

    题目大意

    给出一个树的 (n-1) 条边的权值,问有多少个无向完全图满足其最小生成树的权值为这 (n-1) 个权值。

    (nle 40)

    思路

    并不保证时间复杂度正确!!!

    我们发现我们完全可以 dp,我们可以设 (dp[i][S]) 表示对于前 i 小权值满足其连通块大小集合为 S 的方案数。转移的话直接讨论当前边是否存在即可,如果不存在,说明已经在连通块内部,否则一定在连通块外部。

    时间复杂度玄学,反正能过就行了。

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define mod 1000000007
    #define MAXN 40005
    #define MAXM 1005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    vector <int> cur,S[MAXN];
    map <vector <int>,int> G;
    int n,m,tot,E[MAXN],dp[MAXM][MAXN];
    
    bool flg[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;}
    void Add (int &a,int b){a = add (a,b);}
    
    void dfs (int s,int mx){
    	if (mx == 0){
    		if (s == 0){
    			S[++ tot] = cur,G[cur] = tot;
    			for (Int v : cur) E[tot] = add (E[tot],v * (v - 1) / 2);
    		}
    		return ;
    	}
    	dfs (s,mx - 1);
    	if (s >= mx) cur.push_back (mx),dfs (s - mx,mx),cur.pop_back(); 
    }
    
    signed main(){
    	read (n),m = n * (n - 1) / 2;
    	for (Int i = 1,x;i <= n - 1;++ i) read (x),flg[x] = 1;
    	dfs (n,n),dp[0][1] = 1;
    	for (Int i = 1;i <= m;++ i)
    		for (Int j = 1;j <= tot;++ j){
    			if (!dp[i - 1][j]) continue;
    			int tmp = dp[i - 1][j];
    			if (flg[i]){
    				vector <int> cur = S[j];
    				for (Int x = 0;x < cur.size();++ x)
    					for (Int y = x + 1;y < cur.size();++ y){
    						vector <int> now;
    						now.push_back (cur[x] + cur[y]);
    						for (Int z = 0;z < cur.size();++ z) if (z != x && z != y) now.push_back (cur[z]);
    						sort (now.begin(),now.end(),greater<int>());
    						Add (dp[i][G[now]],mul (cur[x] * cur[y],tmp));
    					} 
    			}
    			else Add (dp[i][j],mul (tmp,E[j] - (i - 1)));
    		}
    	write (dp[m][tot]),putchar ('
    ');
    	return 0;
    }
    
  • 相关阅读:
    阅读ARm芯片手册 阅读方法
    Linux驱动mmap内存映射
    Linux下inotify的基本使用及注意事项
    网络视频监控与人脸识别
    Linux pci驱动源码
    Verilog语法
    跟着我从零开始入门FPGA(一周入门XXOO系列)-1、Verilog语法
    周立功-我的25年嵌入式生涯
    Linux 进程学习
    [转]MFC下关于“建立空文档失败”问题的分析
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13987094.html
Copyright © 2011-2022 走看看