9.27 校内集训 解题报告

版权声明:转载请声明出处,谢谢支持 https://blog.csdn.net/Dreamstar_DS/article/details/82870070

考场上首先觉得C题不是贪心,应该是二分+DP什么的(然而事实上居然就是贪心),觉得B题不可做,于是专攻A题,二分+容斥都已经写好了,但是搞错了一个地方,弄错了一种情况,于是A只有30分
所以这真乃一次惨痛的教训


A. Number

2000ms 65536KB
【description】有 N(2<=N<=15)个数 A1,A2,…,An-1,An,如果在这 N 个数中,有且仅有一个数能整除 m,那么整数 m 就是一个幸运数,你的任务就是在给定 A1,A2,…,An-1,An 的情况
下,求出第 k 小的幸运数。
【input】
第一行为一整数数 N,K(2<=N<=15,1<=K<=2^31-1),意义如上述。
接下来一行有 N 个整数, A1,A2,…,An-1,An,这 N 个整数均不超过2^31-1。
【output】
输出一行,仅包含一个整数 ans,表示第 K 小的幸运数。答案保证不超过10^15。
【sample input】
input1:
2 4
2 3
input2:
2 100
125 32767
【sample output】
output1:
8
output2:
12500
【Data Constraint】
对于50%的数据, N<=5,ANS<=100000
对于80%的数据, N<=10,ANS<=10^15
对于100%的数据, N<=15,ANS<=10^15

我们很容易想到先二分(如果想不到请还仔细观察题目和ANS的数据范围),现在问题便转化成了我们二分出来的答案min中1~mid的幸运数总数,那么这里我们应该用容斥原理,不过请注意题目中一句话

如果在这 N 个数中,有且仅有一个数能整除 m

所以我们要在容斥的板子上做两个改动(而这两个改动我一个没想到一个改错了),首先我们枚举的集合应当是这些集合中数的LCM,否则当原序列中有一个数能整数另一个数的时候就会重复计算某些数,其次我们在最终
ans+= 的时候应该乘上集合中数的个数,由于有且仅有一个数的限制,我们不希望那些数再做出贡献,因此我们把原来所留有的给干干净净地减掉
我用的是队列的容斥板子
AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
ll a[maxn] , n , tot , mi[maxn] , b[maxn] , tmp[maxn] , q[maxn] , k , num[maxn] , ansp;
bool eq , ok , flag[1000];
il ll read(){rg ll x = 0 , w = 1 ; char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) +ch - '0' ; ch = getchar();}return x * w;}
ll gcd(ll a , ll b){
	if (!b) return a;
	return gcd(b , a % b);	
}
ll rc(ll m){
	ll k , t = 0 , ans = 0 , ans2 = 0;
	q[0] = -1;
	for (rg int i = 1 ; i <= n ; ++i){
		k = t;
		for (int j = 0 ; j <= k ; ++j){
			q[++t] = q[j] * a[i] / gcd(abs(q[j]) , abs(a[i]))  * (-1) , num[t] = num[j] + 1;
		}
	}
	for (int i = 1 ; i <= t ; ++i)
		ans += m / q[i] * num[i];
	if (ans <= -1000000000ll) return 1000000000000000ll;
	else return ans;
}
bool check(ll mid){
	eq = 0;
	ll res = rc(mid);
	if (res == k) eq = 1;
	return res >= k;
}
int main(){
	//freopen("number.in" , "r" , stdin);
	//freopen("number.out" , "w" , stdout);
	ansp = 0;
	n = read() , k = read();
	for (rg int i = 1 ; i <= n ; ++i)
		a[i] = read();
	ll l = 1 , r = 1000000000000000ll , ans = -1;
	while (l <= r){
		rg ll mid = (l + r) >> 1;
		if (check(mid)){
			ans = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	cout << ans;
	return 0;	
}

B. TreeCount

1000ms 65536KB
【description】给出一个有 N(2<=N<=1000)个顶点 M(N-1<=M<=N*(N-1)/2)条边的无向连通图。设 dist1[i]表示在这个无向连通图中,顶点 i 到顶点1的最短距离。
现在要求你在这个图中删除 M-(N-1)条边,使得这个图变成一棵树。设 dist2[i]表示在这棵树中,顶点 i 到顶点1的距离。
你的任务是求出有多少种删除方案,使得对于任意的 i,满足 dist1[i]=dist2[i]。
【input】
第一行,两个整数, N, M,表示有 N 个顶点和 M 条边。
接下来有 M 行,每行有3个整数 ,表示顶点 x 和顶点 y 有一条长度为 len 的边。 数据保证不
出现自环、重边。
【output】
输出一个整数,表示满足条件的方案数 mod 2147483647的答案。
【sample input】
input:
3 3
1 2 2
1 3 1
2 3 1
【sample output】
output:
2
【Data Constraint】
对于30%的数据,2<=N<=5,M<=10
对于100%的数据, 2<=N<=1000
1<=len <= 100

好吧我不知道最短路树…不过这道题我们可以借助SPFA OR DJ,我们直接设点i可以被多少个点更新为fi,如果最短路被更新,我们就初始化fi = 1,如果新的路径等于最短路,就fi ++ ,这个可以运用乘法原理,因为当fi为1的时候它对答案数是不做贡献的,而fi > 1的时候,比如fi == 2,那么两条边都是可以选择删去的

AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define mod 2147483647
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1 ; char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) +ch - '0' ; ch = getchar();}return x * w;}
struct edge{
	int v , to , next;	
}e[maxn << 1];
int head[maxn] , cnt , q[maxn] , dis[maxn];
bool vis[maxn];
ll tot , f[maxn];
void add(int u , int v , int w){
	e[++cnt].to = v;
	e[cnt].next = head[u];
	head[u] = cnt;
	e[cnt].v = w;
}
void spfa(int u , int n){
	memset(dis , 127 , sizeof(dis));
	rg int l = 1 , r = 0;
	q[++r] = u;
	vis[u] = 1 , dis[u] = 0;
	for (rg int i = 1 ; i <= n ; ++i) f[i] = 1;
	while (l <= r){
		int now = q[l++];
		for (int i = head[now] ; i ; i = e[i].next){
			int to = e[i].to;
			if (dis[to] > dis[now] + e[i].v){
				dis[to] = dis[now] + e[i].v;
				f[to] = 1;
				if (!vis[to]){
					vis[to] = 1;
					q[++r] = to;
				}
			}
			else if (dis[to] == dis[now] + e[i].v) ++f[to];
		}
		vis[now] = 0;
	}
}

int main(){
	//freopen("treecount.in" , "r" , stdin);
	//freopen("treecount.out" , "w" , stdout);
	int n = read() , m = read() , u , v , w;
	for (rg int i = 1 ; i <= m ; ++i){
		u = read() ,  v = read() , w = read();
		add(u , v , w);
		add(v , u , w);
	}
	spfa(1 , n);
	ll ans = 1;
	for (rg int i = 1 ; i <= n ; ++i)
		ans = ( (ans % mod) * (f[i] % mod)) % mod;
	cout << ans;
	return 0;	
}

C. food

1000ms 65536KB
【description】与很多奶牛一样, Farmer John 那群养尊处优的奶牛们对食物越来越挑剔,随便拿堆草就能打发她们午饭的日子自然是一去不返了。现在, Farmer John 不得不去牧草专供商那里购买大量美味多汁的牧草,来满足他那 N(1 <= N <= 100,000)头挑剔的奶牛。
所有奶牛都对 FJ 提出了她对牧草的要求:第 i 头奶牛要求她的食物每份的价钱不低于 A_i(1<= A_i <= 1,000,000,000),并且鲜嫩程度不能低于 B_i(1 <= B_i <= 1,000,000,000)。商店里供应 M(1 <= M <= 100,000)种不同的牧草,第 i 种牧草的定价为 C_i(1 <= C_i <=1,000,000,000),鲜嫩程度为 D_i (1 <= D_i <= 1,000,000,000)。
为了显示她们的与众不同,每头奶牛都要求她的食物是独一无二的,也就是
说,没有哪两头奶牛会选择同一种食物。
Farmer John 想知道,为了让所有奶牛满意,他最少得在购买食物上花多少
钱。
【input】
第1行: 2个用空格隔开的整数: N 和 M
第2…N+1行: 第 i+1行包含2个用空格隔开的整数: A_i、 B_i
第 N+2…N+M+1行: 第 j+N+1行包含2个用空格隔开的整数: C_i、 D_i
【output】
第1行: 输出1个整数,表示使所有奶牛满意的最小花费。如果无论如何都无法满足所有奶牛
的需求,输出-1
【sample input】
input:
4 7
1 1
2 3
1 4
4 2
3 2
2 1
4 3
5 2
5 4
2 6
4 4
【sample output】
output:
12
【Data Constraint】
对于30%的数据,2<=N<=10, 2<=m <=20
对于100%的数据, 2<=N<=100000, 2<=m<=100000

万万没想到,此题居然真的是贪心…
我们将两个数组(奶牛的和草的)按鲜嫩度由高到低排序,然后对于每头奶牛我们选择出能够满足其鲜嫩度要求的草,然后选取最便宜的即可
当然我们选取是一个lower_bound的过程,而且我们还需要一种数据结构来帮我们删去数组中的数
于是dalao们直接想到了Treap!
蒟蒻还不会树堆,但是我们可以用multiset代替嘛2333(set不支持重复)

AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
il ll read(){rg ll x = 0 , w = 1 ; char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) +ch - '0' ; ch = getchar();}return x * w;}
struct node{
	ll x , y;	
}a[maxn] , b[maxn];
bool cmp(node c , node d){
	return c.y > d.y;
}
multiset<ll> st;
int main(){
	//freopen("food.in" , "r" , stdin);
	//freopen("food.out" , "w" , stdout);
	rg ll n = read() , m = read();
	for (rg int i = 1 ; i <= n ; ++i)
		a[i].x = read() , a[i].y = read();
 	for (rg int i = 1 ; i <= m ; ++i)
 		b[i].x = read() , b[i].y = read();
 	sort(a + 1 , a + 1 + n , cmp);
 	sort(b + 1 , b + 1 + m , cmp);
 	ll now = 1 , ans = 0;
 	for (rg int i = 1 ; i <= n ; ++i){
 		while(a[i].y <= b[now].y && now <= m){
 			st.insert(b[now].x);
			++now;	
 		}
 		multiset<ll>::iterator t = st.lower_bound(a[i].x);
 		if (t == st.end()){
 			puts("-1");
			return 0;	
 		}
 		ans += *t;
 		st.erase(t);
	}
	cout << ans;
	return 0;	
}

猜你喜欢

转载自blog.csdn.net/Dreamstar_DS/article/details/82870070