2023noip赛前20天冲刺 Day7 原神场

溜大了(100+60+50+0)

哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀哇袄🙀

都是原题,那没必要密码了。

A.仙客来

给定 \(n\) 个整数 \(a_i\), 中间用加减号隔开. 你可以在里面任意添加括号, 求能得到的最大结果是多少.

水题,直接贪心即可。

F - チーム分け

#include <bits/stdc++.h>
#define each(i,a,b) for(int i=(a);i<=(b);++i)
#define eachr(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
using namespace std;
constexpr int N=1e5+15;

inline int rd(){
	int w=1,r=0;char c=getchar();
	while(!isdigit(c)) (c=='-')and(w=-1),c=getchar();
	while(isdigit(c)) r=r*10+(c^48),c=getchar();
	return r*w;
}
int n,a[N],tt=1;
ll s[N],ans,sum;

int main(){
	scanf("%d",&n);
	each(i,1,n) a[i]=rd();
	each(i,1,n){
		if(a[i]>=0 && a[i-1]>=0) s[tt]+=a[i];
		else s[++tt]=a[i];
	}
	each(i,1,tt) sum+=abs(s[i]);
	each(i,1,tt-1){
		if(s[i]<0 && s[i+1]>=0) ans=max(ans,sum-2*(-s[i]+s[i+1])),sum+=2*s[i];
		if(s[i]<0 && s[i+1]<0) ans=max(ans,sum-2*(-s[i])),sum+=2*s[i];
	}
	printf("%lld\n",ans);
	return 0;
}

B.白兰花

Minimum Path

给定一个 \(n\) 个点 \(m\) 条边的无向图 \(G\),求 \(G\)\(1\) 到其他点的最短路的长度。

当然,只要求最短路未免过于简单,假设一条路径上的边权依次为 \(w1,w2,\dots,wk\),于是定义 \(S(w)=\min w_i−\max w_i+ \sum w_i\)为路径长度,他希望你能帮他求出在这个定义下的最短路。

实际上就是将路径中的最大值替换成最小值。

将一个点拆成 \(2^2=4\) 个点来表示最大值有没有减,最小值有没有加,一通连边后跑普通的最短路即可。

#include <bits/stdc++.h>
#define each(i,a,b) for(int i=(a);i<=(b);++i)
#define eachr(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
using namespace std;
constexpr int N=1e6+105;
constexpr ll inf=1e18;

inline int rd(){
	int w=1,r=0;char c=getchar();
	while(!isdigit(c)) (c=='-')and(w=-1),c=getchar();
	while(isdigit(c)) r=r*10+(c^48),c=getchar();
	return r*w;
}

struct v2{
	int id;ll w;
	bool operator <(const v2 &o)const{return w>o.w;}
}tmp[N];

struct edge{
	int to,nxt;ll w;
}ed[N<<2];

int head[N],tt;
inline void adde(int u,int v,ll w){
	ed[++tt]={v,head[u],w},head[u]=tt;
}
int n,m,vis[N];
ll dis[N];
priority_queue<v2> q;


inline void add(int u,int v,int w){
	adde(u,v,w);
	adde(u,v+n,0);
	adde(u,v+2*n,2*w);
	adde(u,v+3*n,w);
	adde(u+n,v+n,w);
	adde(u+n,v+3*n,2*w);
	adde(u+2*n,v+2*n,w);
	adde(u+2*n,v+3*n,0);
	adde(u+3*n,v+3*n,w);
}


void dij(){
	q.push({1,0});
	dis[1]=0;
	while(!q.empty()){
		v2 t=q.top();q.pop();
		int u=t.id;
		if(vis[u] || dis[u]!=t.w) continue;
		vis[u]=1;
		for(int i=head[u],v;i;i=ed[i].nxt){
			v=ed[i].to;
			if(dis[u]+ed[i].w<dis[v]){
				dis[v]=dis[u]+ed[i].w;
				if(!vis[v]) q.push(v2{v,dis[v]});
			}
		}
	}
}
int main(){
	//freopen("B/B3.in","r",stdin);freopen("B/B3.out","w",stdout);
	n=rd(),m=rd();
	each(i,1,n*4) dis[i]=inf;
	each(i,1,m){
		int u=rd(),v=rd(),w=rd();
		add(u,v,w);
		add(v,u,w);
	}
	dij();
	each(i,2,n) printf("%lld ",dis[i+3*n]);
	printf("\n");
	return 0;
}

C.月见草

\(n\) 个人, 要分成若干组。对于第 \(i\) 个人,它所在的组的人数不能超过 \(a_i\),求分组的方案数,对 \(998244353\) 取模。

组与组之间是不可区分的,每组的人也是无序的,但人与人之间是可以区分的。

奇妙数数题。

我不会数数,玉玉了。

容易想到将人按权值从大到小考虑,即将 \(a\) 按从大到小排序。

如果从大到小枚举到一个新的人时我们考虑是否将他加入一个新的集合。

这时候前面还没有放入的人的 \(a\) 值是什么已经无关紧要了,因为他们的 \(a\) 值都没有当前这个人的 \(a\) 值小,所以我们可以考虑记 \(f(i,j)\) 表示当前做完第 \(i\) 个人,前面还剩 \(j\) 个人没有加入集合的方案数。但是转移难以去重和优化。

得考虑另一种dp,记 \(f(i,j)\) 表示当前枚举的权值为 \(i\),去完一些大小为 \(i\) 的集合后,权值大于等于 \(i\) 的人还有 \(j\) 个没有分配到集合中的方案数,记 \(p_i\) 为权值为 \(i\) 的人的人数。

于是有转移:

\[f(i,j) = \sum_{k=0}^{\lfloor n/i \rfloor} f(i+1,j-p_i+i\cdot k) \times \frac{1}{k!} \prod_{x=0}^{p-1} \binom{j+i(k-x)}{i} \]

稍微处理一下即可做到 \(\mathcal O(n^2 \log n)\) 而且常数小。

.

.

.

posted @ 2023-10-18 16:21  RoFtaCD  阅读(112)  评论(0)    收藏  举报