赛后——2.22 寒假模拟12


\(\text{T1 Letters}\)

题意

对于给出的 \(n\) 个单词,求有多少个(这 \(n\) 个单词的)子集 \(s\) 使得 \(26\) 个英文字符中的每个字符都在 \(s\) 中的某个单词中出现过了。

数据范围:\(n\le 25\)

思路

\(n\) 很小可以暴搜,预处理出来每个单词的字符包含情况,状态压成一位存储,接着我们只需要判断最终的状态是否为 \(2^{26}-1\) 的即可。

考虑优化,因为存在一些单词是必选的,它们拥有独一无二的字符,于是可以将它们在输入时找出,于是搜索时就不考虑单词的选取情况了。

代码

点击查看代码
int n;
char s[30][105];
vector G[30];
int sit[105],vis[105];
int S,ans;
#define cr (1<<26)-1
inline void dfs(int x,int st){
	if(x>n){
		if(st==cr) ans++;
		return;
	}
	int tmp=x+1;
	while(vis[tmp]&&tmp<=n) tmp++;
	if(!x) dfs(tmp,S);
	else{
		dfs(tmp,st);
		dfs(tmp,st|sit[x]);
	}
} 
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
	}
	for(int i=1;i<=n;i++){
		int len=strlen(s[i]+1);
		for(int j=1;j<=len;j++){
			G[s[i][j]-'a'+1].push_back(i);
			sit[i]|=(1<<(s[i][j]-'a'));
		}
	}
	for(int i=1;i<=26;i++){
		if(G[i].size()==0){
			printf("0\n");
			return 0;
		}
		if(G[i].size()==1){
			S|=sit[G[i][0]];
			vis[G[i][0]]=1;
		}
	}
	dfs(0,0);
	printf("%d\n",ans);
	return 0;
}

\(\text{T2 Circle}\)

题意

平面上有一些圆,第 \(i\) 个圆的坐标为 \((x_i,0)\),半径为 \(r_i\)。求这些圆把平面化分成了多少块区域(最外面的也算)。

保证任意两圆不相交,但可以相切。

思路

不难发现,每个圆把包含它的大圆分成了内外两部分(最外面可以视作一个无穷大的圆),于是如果没有相切情况,答案为 \(n+1\)

接着,对于若干个小圆相接拦断一个大圆(即相切)的情况,答案为 \(+1\),我们需要统计这种情况的个数。

首先对所有圆进行排序,令每个圆的右端点,即 \(x_i+r_i\) 为第一关键字升序排序,令每个圆的半径 \(r_i\) 为第二关键字升序排序,这样我们会发现,右端点在小圆右侧或半径大于小圆的圆都排在了小圆的后面,换句话说,就是枚举到小圆之前,已经枚举了小圆内部所有的圆,发现当计算完这个小圆后,其内部的圆就已经对后续的答案没有影响了,可以统一合并成这个小圆了。

用单调栈来维护,每次把所有小圆内部的圆都弹栈并累计半径和,如果半径和等于小圆半径,说明出现了“拦腰截断”的情况,答案 \(+1\)

代码

点击查看代码
int n;
struct node{
	int x,r,lpos,rpos;
	bool operator <(const node &rhs)const{
		if(rpos==rhs.rpos) return r s;
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i].x=read(),a[i].r=read();
		a[i].lpos=a[i].x-a[i].r;
		a[i].rpos=a[i].x+a[i].r;
	}
	ans=n+1;
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		int sum=0;
		while(!s.empty()&&a[s.top()].lpos>=a[i].lpos&&a[s.top()].rpos<=a[i].rpos){
			sum+=a[s.top()].r;
			s.pop();
		}
		s.push(i);
		if(sum==a[i].r){
			ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

\(\text{T3 Bag}\)

题意

\(n\) 个物品,第 \(i\) 个物品的价值为 \(w_i\),重量为 \(c_i\),有 \(k\) 个背包,第 \(i\) 个背包里的容量为 \(v_i\),每个背包只能装一件物品,求能装走的最大价值。

思路

因为每个背包只能有装一件物品,于是贪心,弄一个 \(\text{multiset}\) 来维护背包接着二分一下就可以了。

代码

点击查看代码
int n,k;
struct node{
	ll c,w;
	bool operator <(const node &rhs)const{
		if(w==rhs.w) return crhs.w;
	}
}a[maxn];
multiset s;
ll ans,cnt;
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++){
		a[i].c=read(),a[i].w=read();
	}
	for(int i=1;i<=k;i++){
		ll v=read();
		s.insert(v);
	}
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		multiset::iterator it=s.lower_bound(a[i].c);
		if(it!=s.end()){
			s.erase(it);
			ans+=a[i].w;
			cnt++;
		}
		if(cnt==k){
			printf("%lld\n",ans);
			return 0;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

\(\text{T4 Forest}\)

题意

有一只猴在树林间蹦跶。

森林里有 \(n\) 棵树,第 \(i\) 棵树的高度是 \(E_i\)\(m\) 条蹦跶的路径,从第 \(A_i\) 棵树蹦跶到第 \(B_i\) 棵树花费 \(T_i\) 的时间。

当它从高度为 \(s\) 的位置蹦跶到另一棵树上且花费了 \(t\) 的时间后,他会梦到到目标树上高度为 \(s-t\) 的位置,\(s-t\) 不能为负,也不能超过目标树的高度。

它可以花一单位时间爬上一单位高度或者爬下一单位高度。

它初始在第 \(1\) 棵树,高度为 \(X\),问它到达第 \(n\) 棵树的顶部所需的最少时间。

思路

首先是 \(40%\) 的部分分直接跑最短路,结果乘上 \(2\) 再加上 \(E_n\) 即可。

接着我们发现,走过一条消耗 \(t\) 的路径时,就会下降 \(t\) 个单位高度,不妨把所有消耗都变成纵向的高度,于是我们每次跳跃有两种选择,选择直接跳过去或者是需要向下一点直到能跳到下一棵树的树顶,当然需要特判出来树高小于路径长的情况。

于是我们维护出的,实际上是全部转移成纵向高度后下降到的位置,用最短路算法可以得出这个 \(dis_n\),发现这时题目就变成了从 \(x\) 走到 \(dis_n\) 的高度(其实是走到了第 \(n\) 棵树的底下),接着向上走到树顶。

于是答案为:\(X-dis_n+E_n-dis_n\)

我们再用图解释一下,正常路线是红色路线,平移一下可以得到蓝色路线,因为这条蓝色路线中斜方向的长度是等于任意一条绿色路线的,而绿色路线是 \(X-dis_n\),蓝色路线竖直方向的长是 \(E_n-dis_n\),于是答案:\(X-dis_n+E_n-dis_n\)

代码

点击查看代码
int n,m;
ll x,e[maxn];
vector E[maxn];
struct node{
	int u,d;
	bool operator<(const node &rhs)const{
		return d q;
	tmp.u=1,tmp.d=x;
	q.push(tmp);
	while(!q.empty()){
		tmp=q.top();
		q.pop();
		int u=tmp.u,d=tmp.d;
		if(dis[u]!=-llinf) continue;
		dis[u]=d;
		for(int i=0;ie[u]) continue;
			tmp.u=v,tmp.d=min(d-w,e[v]);
			q.push(tmp);
		}
	}
}
int main(){
	n=read(),m=read(),x=read();
	for(int i=1;i<=n;i++){
		e[i]=read();
	}
	for(int i=1;i<=m;i++){
		int u=read(),v=read(),w=read();
		E[u].push_back(make_pair(v,w));
		E[v].push_back(make_pair(u,w));
	}
	Dijkstra();
	if(dis[n]==-llinf){
		printf("-1\n");
	}
	else{
		printf("%lld\n",x-dis[n]+e[n]-dis[n]);
	}
	return 0;
}