ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

暑假集训6

2022-08-19 07:00:08  阅读:102  来源: 互联网

标签:cnt int long hu maxn 暑假 include 集训


前两题只会打暴力,本来以为又要垫底了,结果还可以?

A. 接力比赛

确实是背包,排序后每次跑上界为\(\sum w_i\),然后刚刚好卡过??

随机数据跑的还是挺快的

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<iostream>
#include<set>
#include<map>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 1005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
inline int read(){
	int x = 0; char c = getchar(); bool f = 0;
	while(c < '0' || c > '9'){if(c == '-')f = 1;c = getchar();}
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	if(f)x = -x; return x;
}
ll f[maxn * maxn], g[maxn * maxn], sum[maxn];
int n, m;
struct node{int v, w;}d[maxn];
bool cmp(node x, node y){return x.w < y.w;}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = read(); m = read();
	for(int i = 1; i <= n; ++i)d[i].w = read(), d[i].v = read();
	sort(d + 1, d + n + 1, cmp);
	for(int i = 1; i <= n; ++i)sum[i] = sum[i - 1] + d[i].w;
	for(int i = 1; i <= sum[n]; ++i)f[i] = -inf;
	for(int i = 1; i <= n; ++i)
		for(int j = sum[i]; j >= d[i].w; --j)
			f[j] = max(f[j - d[i].w] + d[i].v, f[j]);
	
	for(int i = 1; i <= m; ++i)d[i].w = read(), d[i].v = read();
	sort(d + 1, d + m + 1, cmp);
	for(int i = 1; i <= m; ++i)sum[i] = sum[i - 1] + d[i].w;
	for(int i = 1; i <= sum[m]; ++i)g[i] = -inf;
	for(int i = 1; i <= m; ++i)
		for(int j = sum[i]; j >= d[i].w; --j)
			g[j] = max(g[j - d[i].w] + d[i].v, g[j]);
	ll ans = 0;
 	for(int i = 1; i <= sum[m]; ++i)ans = max(ans, f[i] + g[i]);
	printf("%lld\n",ans); 
	return 0;
}

B. 树上竞技

对每条边求贡献,设子树大小为\(s\),该边贡献为

\(\large \sum_{i = 1}^{m - 1}(^s_i)(^{n-s}_{m- i})min(i,m-i)\)

拆开\(min\),设\(k = (m - 1) / 2\)

\(\large f_s = \sum_{i = 1} ^ {k}(^s_i)(^{n - s}_{m - i})i\)

那么当前贡献为\(\large f_s + f_{n - s} + [m \%2 == 0](^s_{m/2})(^{n - s}_{m/2})m/2\)

组合数可以搞出来\(s g_s = f_s\)

\(\large g_s = \sum_{i = 1}^{k}(^{s - 1}_{i - 1})(^{n - s}_{m - i})\)

上项和一直为\(n - 1\),下面和为\(m - 1\),可以有这样的组合意义

从\(n - 1\)个物品选\(m - 1\)个,前\(s - 1\)个物品最多选\(k - 1\)个

转移有
$\large g_s - g_{s + 1} =(^{s - 1}_{k - 1}) (^{n - s - 1} _{m - k - 1}) $

考虑减去不合法的方案,在前\(s-1\)位置中选了\(k-1\)个,并且选了\(s\)位置的方案是不合法的,减去得到当前方案

式子挺玄乎的

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<iostream>
#include<set>
#include<map>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 1000005;
const int mod = 1000000007;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9'){c = getchar();}
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
struct edge{int to, net;}e[maxn << 1 | 1];
int head[maxn], tot;
void add(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}
int fac[maxn], inv[maxn];
int c(int n, int m){if(n < m || n < 0 || m < 0)return 0;else return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int n, m, size[maxn], cs[maxn];
ll ans;
void dfs(int x, int fa){
	size[x] = 1;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa)continue;
		dfs(v, x);
		size[x] += size[v];
	}
	++cs[size[x]];
}
int g[maxn];
int main(){
	freopen("meeting.in","r",stdin);
	freopen("meeting.out","w",stdout);
	n = read(), m = read();
	fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod; inv[0] = 1; 
	for(int i = 2; i <= n; ++i){int u = read(); add(u, i); add(i, u);}
	dfs(1, 0);
	int k = (m - 1) / 2;
	if(k)g[1] = c(n - 1, m - 1);
	for(int s = 1; s <= n; ++s)g[s + 1] = (g[s] - 1ll * c(s - 1 , k - 1) * c(n - s - 1, m - k - 1) % mod + mod) % mod;
	for(int s = 1; s <= n; ++s)g[s] = 1ll * g[s] * s % mod;
	for(int s = 1; s <= n; ++s)if(cs[s]){
		ll now = (g[n - s] + g[s]) % mod;
		if(m % 2 == 0)now = (now + c(s, m / 2) * 1ll * c(n - s, m / 2) % mod * (m / 2) % mod) % mod;
		ans = (ans + now * cs[s] % mod) % mod;
	}
	printf("%lld\n",ans);
	return 0;
}

C. 虚构推理

正解二分,用滑动窗口类似的方式找交集,(我觉得有点扫描线思想)

懒得打了,可以暴力枚举时间,每次\(lower\_bound\)找最远 的针即可

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<iostream>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 100005;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9'){c = getchar();}
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
double sz[maxn], fz[maxn];
int n;
int main(){ 
	freopen("unreal.in","r",stdin);
	freopen("unreal.out","w",stdout);
	n = read();
	int cnt = 0;
	for(int i = 1; i <= n; ++i){
		double h = read(), m = read(), s = read();
		if(h >= 12)h -= 12;
		fz[++cnt] = m * 6 + s / 10; 
		sz[cnt] = h * 30 + m / 2 + s / 120;
		fz[++cnt] = m * 6 + s / 10 + 360; 
		sz[cnt] = h * 30 + m / 2 + s / 120 + 360;
	}
	sort(fz + 1, fz + cnt + 1);
	sort(sz + 1, sz + cnt + 1);
	double ans = 0x3f3f3f3f3f;
	fz[0] = sz[0] = -0x3f3f3f3f3f; fz[cnt + 1] = sz[cnt + 1] = 0x3f3f3f3f3f;
	for(double h = 0; h < 360; h += 30){
		for(double mi = 0; mi < 360; mi += 0.001){
			double hu = h + mi / 12; if(hu < 180) hu = hu + 360;
			int p = lower_bound(sz + 1, sz + cnt + 1, hu - 180) - sz;
			int q = lower_bound(sz + 1, sz + cnt + 1, hu + 180) - sz - 1;
			double nans = max(sz[q] - hu, hu - sz[p]);
			hu = mi; if(hu < 180) hu += 360;
			p = lower_bound(fz + 1, fz + cnt + 1, hu - 180) - fz;
			q = lower_bound(fz + 1, fz + cnt + 1, hu + 180) - fz - 1;
			nans = max(nans, max(fz[q] - hu, hu - fz[p]));
			ans = min(ans, nans);
		}
	}
	printf("%lf\n",ans);
	return 0;
}

D. 记忆碎片

褐的\(Delov\)大佬的题解,讲的确实很好(我写题解好像会引流啊QAQ)

\(dp_{i,s}\)表示添加了\(i\)条树边,状态为\(s\)的方案数

两个状态不同,当且仅当存在某个大小的联通块数量不同,我们只关心某个大小的联通块有多少

考虑加入一条树边,我们把两个联通块并成一个,设状态中两个联通块分别有\(cnt_a, cnt_b\)个,大小为\(a ,b\)

那么他的贡献就是\(dp_{i - 1}{las} * cnt_a * cnt_b * a * b\)

特判\(a == b\)(会取重)

考虑加入非树边

非树边不会改变联通性

把联通块扫一遍,可以得到一共有多少条联通块内的边

减去已经加上的边的数量,就是多少位置可以加边,设其为 \(sum\),

那么对于第一条非树边,它有\(sum\)种选择方案,第二条有\(sum - 1\)种......这是\(sum^{\frac{cnt}{}}\)

现在剩下最后的问题是如何分状态,直接\(DFS\)拆分的话会炸,因为存在重复状态,所以我们写一个 \(hash\)函数,用\(map\)维护已经出现的状态的\(hash\)值对状态的映射,这样就能愉快的去重了

我这里使用了\(delov\)大佬的\(bfs\)写法,在预处理状态时顺便把转移边建了出来,方便后面操作,使用\(bitset\)貌似可以优化代码常数,但是实际上每次爆扫好像跑的更快?

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<iostream>
#include<set>
#include<map>
#include<bitset>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 42;
const ull base = 233;
const ull mod = 1e9 + 7;
const ll inv2 = 500000004;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9'){c = getchar();}
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int qpow(int x, int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int fac[5005], inv[5005];
void pre(){
	int up = 1600;
	fac[0] = 1; for(int i = 1; i <= up; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[up] = qpow(fac[up], mod - 2); for(int i = up - 1; i; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod; inv[0] = 1; 
}
int down(int n, int m){return 1ll * fac[n] * inv[n - m] % mod;}
int cnt, n;
struct statue{
	bitset<44> b;
	int rem[maxn];
	void del(int x, int val){rem[x] -= val; if(!rem[x])b[x] = 0;}
	void add(int x, int val){if(!rem[x] && val)b[x] = 1; rem[x] += val;}
	ull	get_hash(){
		ull ans = 0;
		for(int i = 1; i <= n; ++i)ans = (ans * base + rem[i]) % mod;
		return ans; 
	}
}zt[50005];
int v[maxn];
map<ull, int>mp;
queue<int>q;
int head[45535], tot;
struct edge{int to, net, sizex, sizey;}e[4000005];
void add(int u, int v, int sx, int sy){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
	e[tot].sizex = sx;
	e[tot].sizey = sy;
}
void get_trans(){
	cnt = 1;
	zt[1].add(1, n);
	q.push(1); 
	mp[zt[1].get_hash()] = 1;
	while(!q.empty()){
		int x = q.front(); q.pop();
		statue nzt = zt[x];
		for(int now = zt[x].b._Find_first(); now <= n && zt[x].b[now]; now = zt[x].b._Find_next(now)){
			if(zt[x].rem[now] > 1){
				nzt.del(now, 2);
				nzt.add(now + now, 1);
				if(!mp[nzt.get_hash()]){zt[++cnt] = nzt; q.push(cnt); mp[nzt.get_hash()] = cnt;}
				add(x, mp[nzt.get_hash()], now, now);
				nzt.del(now + now, 1); nzt.add(now, 2);
			}
			nzt.del(now, 1);
			for(int nxt = zt[x].b._Find_next(now); nxt <= n && zt[x].b[nxt]; nxt = zt[x].b._Find_next(nxt)){
				nzt.del(nxt, 1); nzt.add(now + nxt, 1);
				if(!mp[nzt.get_hash()]){zt[++cnt] = nzt; q.push(cnt); mp[nzt.get_hash()] = cnt;}
				add(x, mp[nzt.get_hash()], now, nxt);
				nzt.add(nxt, 1); nzt.del(now + nxt, 1);
			}
			nzt.add(now, 1);
		}
	}
}
int dp[43][40005];

int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n = read(); pre();
	for(int i = 1; i < n; ++i)v[i] = read();
	sort(v + 1, v + n);
	get_trans();
	dp[0][1] = 1;
	for(int now = 1; now < n; ++now){
		if(now > 1)
			for(int i = 1; i <= cnt; ++i)if(dp[now - 1][i]){
				int sum = 0;
				for(int x = zt[i].b._Find_first(); x <= n && zt[i].b[x]; x = zt[i].b._Find_next(x))
					sum = (sum + 1ll * x * (x - 1) / 2 * zt[i].rem[x] % mod) % mod;
				sum = (sum - v[now - 1] + mod) % mod;
				dp[now - 1][i] = 1ll * dp[now - 1][i] * down(sum, v[now] - v[now - 1] - 1) % mod;

			}
		for(int i = 1; i <= cnt; ++i)if(dp[now - 1][i]){
			for(int j = head[i]; j; j = e[j].net){
				int v = e[j].to;
				int sx = e[j].sizex, sy = e[j].sizey;
				if(sx != sy)dp[now][v] = (dp[now][v] + 1ll * dp[now - 1][i] * zt[i].rem[sx] % mod * zt[i].rem[sy] % mod * sx % mod * sy % mod) % mod;
				else dp[now][v] = (dp[now][v] + 1ll * dp[now - 1][i] * zt[i].rem[sx] % mod * (zt[i].rem[sx] - 1) % mod * inv2 % mod * sx % mod * sx % mod) % mod;
			}
		}
	}
	int end; for(int i = 1; i <= cnt; ++i)if(zt[i].b[n]){end = i; break;}
	int res = n * (n - 1) / 2 - v[n - 1];
	int ans = 1ll * dp[n - 1][end] * fac[res] % mod;
	printf("%d\n",ans);
	return 0;
}

标签:cnt,int,long,hu,maxn,暑假,include,集训
来源: https://www.cnblogs.com/Chencgy/p/16600340.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有