杂题做题记录

归不到类里面的题放这里来,可能主要是贪心题。

P7913 [CSP-S 2021] 廊桥分配

贪心题,重要是问题的转化:珂以给舰桥都标上编号,每有一架飞机抵达就进入当前空闲的编号的舰桥,如果舰桥满了那么就往后排。这个性质就是解题关键,相当于是一个小小的贪心选择,那么我们就可以先预处理出每架飞机会停在哪个舰桥,记录两个数组 res1 i,res2 i 表示国内和国外安排 i 个舰桥时的最大珂容飞机数。

至于维护,参考了题解中的优先队列,用一个小根堆来维护舰桥的出入情况,还能保证复杂度。这个题还是有点神奇的,思路珂以较简单转化但是实现有一定难度。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair <ll,ll> pi;
const ll N=114514,M=1919810;
ll n,m1,m2;
struct xx{
	ll a,b;
}w[N],v[N];
bool cmp(xx x,xx y){
	return x.a<y.a;
}
ll res1[N],res2[N];
void solve1(){
	priority_queue<pi,vector<pi>,greater<pi> > p;
	priority_queue<ll,vector<ll>,greater<ll> > q;
	//等待离港队列、空闲廊桥队列
	for(int i=1;i<=n;++i) q.push(i);
	for(int i=1;i<=m1;++i){
		while(!p.empty()&&w[i].a>=p.top().first){
			q.push(p.top().second);
			p.pop();
		}
		if(!q.size()) continue;
		ll u=q.top(); q.pop();
		res1[u]++; //它可以上舰 
		p.push(make_pair(w[i].b,u));
	}
	for(int i=1;i<=n;++i) res1[i]+=res1[i-1]; //做前缀和 
}
void solve2(){
	priority_queue<pi,vector<pi>,greater<pi> > p;
	priority_queue<ll,vector<ll>,greater<ll> > q;
	for(int i=1;i<=n;++i) q.push(i);
	for(int i=1;i<=m2;++i){
		while(!p.empty()&&v[i].a>=p.top().first){
			q.push(p.top().second);
			p.pop();
		}
		if(!q.size()) continue;
		ll u=q.top(); q.pop();
		res2[u]++;
		p.push(make_pair(v[i].b,u));
	}
	for(int i=1;i<=n;++i) res2[i]+=res2[i-1];
}
int main(){
	cin>>n>>m1>>m2;
	for(int i=1;i<=m1;++i) cin>>w[i].a>>w[i].b;
	for(int i=1;i<=m2;++i) cin>>v[i].a>>v[i].b;
	sort(w+1,w+m1+1,cmp);
	sort(v+1,v+m2+1,cmp); //不知道不排序会不会死
	solve1();
	solve2();
	ll ans=0;
	for(int i=0;i<=n;++i) ans=max(ans,res1[i]+res2[n-i]);
	cout<<ans;
	return 0;
}

P7915 [CSP-S 2021] 回文

是这样的,我们珂以比较是先取 L 还是先取 R 的字典序更小(也只有这两者),先讨论取 L 的情况,后者同理。

我们取了 1 位置的数之后设和他相同的另一个数的位置为 pos,那么珂以把剩余的序列分成 [2 ,pos)[pos,2 n] 两个栈,然后依次取出栈顶元素。我们可以发现能取出一个数当且仅当他处在一个栈的栈顶和另一个栈的栈底(已经取出来的数不算),然后当一个栈取不了时就换另一个栈。不过为什么char型清零是 ans[i]=0 啊?神奇

和上一道题一样,难点在于实现。

不知道为什么发了好久的呆,你还记得自己说了什么吗/qd

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1145140,M=1919810;
ll T,n,a[N];
ll s1[N],s2[N],top1,top2,pos1,pos2;
char ans[N];
//l、y栈顶,r、x栈底 
bool calc(ll l,ll r,ll x,ll y){
	for(int i=1;i<n;++i){
		if(l<=r&&((l<r&&a[l]==a[r])||(x<=y&&a[l]==a[x]))){
			if(l<r&&a[l]==a[r]){
				++l,--r;
				ans[i]='L',ans[2*(n-1)-i+1]='L';
			}
			else{
				++l,++x;
				ans[i]='L',ans[2*(n-1)-i+1]='R';
			}
		}
		else if(x<=y&&((x<y&&a[x]==a[y])||(l<=r&&a[r]==a[y]))){
			if(x<y&&a[x]==a[y]){
				++x,--y;
				ans[i]='R',ans[2*(n-1)-i+1]='R';
			}
			else{
				--r,--y;
				ans[i]='R',ans[2*(n-1)-i+1]='L';
			}
		}
		else return 0;
	}
	return 1;
}
int main(){
	cin>>T;
	while(T--){
		cin>>n; top1=top2=pos1=pos2=0;
		for(int i=1;i<=2*n;++i){
			cin>>a[i];
			if(a[i]==a[1]) pos1=i;
		}
		for(int i=1;i<2*n;++i)
			if(a[i]==a[2*n]){pos2=i;break;}
		if(calc(2,pos1-1,pos1+1,2*n)) printf("L%sL\n",ans+1); //好像首尾确实处理不到 
		else if(calc(1,pos2-1,pos2+1,2*n-1)) printf("R%sL\n",ans+1); //换种写法应该能行 
		//woshishabi
		else printf("-1\n");
		for(int i=1;i<=2*n;++i) ans[i]=0; //char用0清空? 
	}
	return 0;
}

Artem and Array

智慧题++

我们先尝试发掘一些性质,首先两端的数肯定是不会删的(删了就会导致少一个能加的数),然后是如果一个数小于等于他左右两边的数,那么先删他肯定是最优的,把这一类的数删完以后我们就将剩余的数从小到大排序然后取前 m2 个数就是答案,现在的问题是怎么维护前一类特殊的数。

我们把某一个位置的数删了以后珂以把他设成 inf,然后我们设一个 last[i] 记录 i 前面第一个有值的位置,设当前左中右三个位置分别为 pos1 ,pos2 ,i,如果当前可以更新那么把 a[pos2 ] 赋成 inf,然后因为删完 pos2 可能会让原来的 pos 1 变成特殊的数,所以我们要用一个while来不断向前迭代:last[i]=pos1 ,pos2 =pos1 ,pos1 =last[i],如果当前的 pos2 不是特殊的数了就退出。这样子比起我们原来直接暴力一个一个往前找就快多了。还是不难,想一想就出来了。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=5*114514,M=1919810,inf=1145141919;
ll n,m,a[N],b[N],ans,las[N]; //记录上一个有值的位置 
int main(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	if(n<=2){
		cout<<0;
		return 0;
	}
	for(int i=1;i<=n;++i) las[i]=i-1;
	for(int i=3;i<=n;++i){
		ll pos2=las[i],pos1=las[pos2];
		if(a[pos2]<=a[i]&&a[pos2]<=a[pos1]){
			while(114514){
				if(a[pos2]>a[i]||a[pos2]>a[pos1]) break;
				ans+=min(a[i],a[pos1]);
				a[pos2]=-inf;
				//我们要删掉pos2 
				las[i]=pos1;
				pos2=pos1,pos1=las[pos1];
			}
		}
	}
	for(int i=1;i<=n;++i)
		if(a[i]>0) b[++m]=a[i];
	sort(b+1,b+m+1);
	for(int i=1;i<=m-2;++i) ans+=b[i];
	cout<<ans;
	return 0;
} 

Decinc Dividing

思维题,难,做法很多,珂以当结论题也珂以纯dp,dp做法放到别的地方去了。

还是先尝试发掘一些性质,我们珂以尝试将数列的变化情况画成一个折线图,当我们列举出一些“不好的”情况,我们发现他们都带有一种珂以将大小关系表示成 3412 或者 2143 的这种由两个不相交的单增/减序列组成的子串,于是我们可以猜测不带有这种字串的情况都是“好的”。那么现在我们的问题是怎么求出这个子串的出现情况,dp/ds?

我们要处理出每个左端点对应的大小关系呈上述情况的最小右端点,然后倒着再处理一遍(处理 2143),最后取出两个答案的 max。具体而言,我们就以这个 3412 为例:对于 3,只需要考虑最近的 4,因此可以在每个 4 的位置枚举 3 计算答案,同时在右侧找到这个 3 对应的最近的 2。实现时可以先对于每个 2 先找到最靠近的 1,在 1 加入时更新 2 的位置;再对于每个 3 找到最靠近的 4,在 4 加入时枚举 3 查询最近的 2 。需要用数据结构维护小于某一个值的最小位置,不难用树状数组处理(都是多头说的)。

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2*114514,M=1919810,inf=114514919810;
ll n,a[N],ans,res[N],c[N];
ll lowbit(ll x){
	return x&-x;
}
void add(ll x,ll k){
	while(x<=n){
		c[x]=min(c[x],k);
		x+=lowbit(x);
	}
}
ll query(ll x){
	ll ans=n+1;
	while(x){
		ans=min(ans,c[x]);
		x-=lowbit(x);
	}
	return ans;
}
stack <ll> s;
vector <ll> g[2][N]; //增/减
//部分细节和dp做法应该差不多 
void solve(){
	for(int i=1;i<=n+14;++i) c[i]=n+1;
	while(s.size()) s.pop();
	for(int i=1;i<=n;++i){
		while(s.size()&&a[i]<a[s.top()]) s.pop();
		if(s.size()) g[0][s.top()].push_back(i);
		s.push(i);
	}
	while(s.size()) s.pop();
	for(int i=n;i>=1;--i){
		while(s.size()&&a[i]>a[s.top()]) s.pop();
		if(s.size()) g[1][s.top()].push_back(i);
		s.push(i);
	}
	for(int i=n;i>=1;--i){
		for(int j:g[0][i]) add(a[j],j);
		for(int j:g[1][i]) res[j]=min(res[j],query(a[j]-1));
		g[0][i].clear(),g[1][i].clear();
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i],res[i]=n+1;
	res[n+1]=n+1;
	solve(); //找3412
	for(int i=1;i<=n;++i) a[i]=n-a[i]+1; //注意是将排列中的大小关系反转 
	solve(); //倒着再找一遍,相当于找2143
	for(int i=n;i>=1;--i){
		res[i]=min(res[i],res[i+1]);
		ans+=res[i]-i;
	}
	cout<<ans;
	return 0;
}

CF452&P2757 [国家集训队] 等差子序列

双倍经验。一个比较正常的解法是线段树维护哈希,但是由于一些奇妙而难以证明的性质我们直接暴力做就完了。
首先,我们设等差数列的公差为 k,当前的数的位置为 i,那么我们就是要去判断 aikai+k 所在的位置是不是在 i 的异侧。考虑怎么维护,我们另外记一个序列 f,每次把 fai 标记为 1,这样子标记后发现直接判断当前序列是不是回文串就行了。

posted @   和蜀玩  阅读(4)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示