首页 > 其他分享 >[Codeforces Round 857 (Div. 1)][Codeforces 1801A~1801G(部分)]

[Codeforces Round 857 (Div. 1)][Codeforces 1801A~1801G(部分)]

时间:2023-03-10 09:33:46浏览次数:55  
标签:1801A 857 int Codeforces 物品 序列 oplus maxA 演唱会

FST哩,好似!本来能+80的,现在只加了30,相当于掉了50分捏

1801A - The Very Beautiful Blanket

题目大意:要求构造一个 \(n\times m\) 的矩阵 \(B\),使得对任意一个 \(4\times 4\) 的子矩阵 \(A\) 均满足 \(A_{13} \oplus A_{14} \oplus A_{23} \oplus A_{24} = A_{31} \oplus A_{32} \oplus A_{41} \oplus A_{42}\) 且 \(A_{13} \oplus A_{14} \oplus A_{23} \oplus A_{24} = A_{31} \oplus A_{32} \oplus A_{41} \oplus A_{42}\)

直接令 \(A_{ij}=256\times i+j\) 即可,原理是这样构造可以让任意一个 \(2\times 2\) 的子矩阵满足异或和为零,因为在二进制下对应权值最低的八位代表列号,更高位代表行号,各部分都是能各自消掉的。

#include<bits/stdc++.h>
using namespace std;
#define N 300
int T,n,m,a[N][N];
int main()
{
	for(int i=0;i<256*256;i++){
		int x=i/256,y=i%256;
		a[x][y]=i;
	}
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		printf("%d\n",n*m);
		for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			printf("%d%c",a[i][j],j<m-1?' ':'\n');
	}
}

1801B - Buying gifts

题目大意:有 \(n\) 个物品,每个物品有两个权值 \(a_i,b_i\)分别表示该物品给 \(A,B\) 两个人的权值。要求把这 \(n\) 个物品分配给两人使得 \(|maxA-maxB|\) 尽可能小。\(maxA,maxB\) 分别表示两人分配到物品的最大权值,每人至少要被分配一个物品。

这题我 FST 了,原因是当所有物品一样的时候我全买给了 \(A\),好似!

这种题的做法有一个惯用套路就是固定其中一维然后求解。假设我们钦定了 \(maxA\) 的值,那么可以发现所有 \(a_i>maxA\) 的物品都必须给 \(B\),而这时其它物品怎么选都不会影响 \(maxA\) 的值,那么在这些物品中选一个 \(b_i\) 与 \(maxA\) 最接近的给 \(B\) 即可。

于是就得到了一种做法,先把物品按照 \(a_i\) 排序,从小到大枚举钦定哪一个物品是必须给 \(A\) 的,这时后面的所有物品就都得给 \(B\),可以预处理后缀 \(\max\) 求出这一部分对 \(maxB\) 的贡献。而对于前面选离 \(maxA\) 最近的值,利用 \(\mathrm{multiset}\) 中的 \(\mathrm{lower\_bound}\) 乱搞即可。

注意由于 \(a_i\) 的值可能相同,所以需要对相同的 \(a_i\) 分批处理,具体实现见代码。

\(\mathrm{Find}\) 函数中的 \(o\) 就是特判之前 FST 的情况用的。

#include<bits/stdc++.h>
using namespace std;
#define N 500010
int n,f[N],ans;
multiset<int>s;
struct rua
{
	int x,y;
	void read(){scanf("%d%d",&x,&y);}
	bool operator <(const rua &t)const{return x<t.x;}
}a[N];
int Find(int x,int y,int o)
{
	int res=o?1e9:abs(x-y);
	auto it=s.lower_bound(x);
	if(it!=s.end()){
		int z=max(y,*it);
		res=min(res,abs(x-z));
	}
	if(it!=s.begin()){
		it--;
		int z=max(y,*it);
		res=min(res,abs(x-z));
	}
	return res;
}
void init()
{
	ans=2e9;
	s.clear();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)a[i].read();
	sort(a+1,a+n+1);
	f[n+1]=0;
	for(int i=n;i>=1;i--)f[i]=max(a[i].y,f[i+1]);
	for(int i=1,j;i<=n;i=j+1){
		j=i;
		while(j<=n && a[j].x==a[i].x)j++;j--;
		int x=a[i].x;
		int y=f[j+1];
		for(int k=i;k<=j;k++)s.insert(a[k].y);
		for(int k=i;k<=j;k++){
			s.erase(s.find(a[k].y));
			ans=min(ans,Find(x,y,j==n));
			s.insert(a[k].y);
		}
	}
	printf("%d\n",ans);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)init();
}

1801C - Music Festival

题目大意:定义一个序列 \(A\) 的价值为满足 \(A_i=\max_{j=1}^{i}A_j\) 的 \(i\) 的个数。给定 \(n\) 个序列,要求以任意方式将他们拼接起来,使得得到的序列价值最大。

首先可以得到,对于每个序列,只有满足对应性质的那些元素才有可能对答案产生贡献,所以首先可以在输入的时候就对序列进行预处理,留下一个严格递增的序列。

考虑 \(\mathrm{DP}\),令 \(f_i\) 表示以数字 \(i\) 为结尾的序列的最大价值,那么可以得到如果把某个序列 \(A\) 接在尾部,对应的方案只能更新到 \(f_{maxA}\) 上。于是考虑把序列按照最后一个元素大小进行排序。

在 \(\mathrm{DP}\) 的过程中,考虑对每个序列枚举该序列是从第几个位置开始产生贡献的。假设当前在序列 \(A\) 中枚举到的位置为 \(i\)(这题本人实现下标是从 \(0\) 开始),序列处理后的长度为 \(k\),最大值为 \(m\),那么找到最大的 \(j\) 使得 \(j<a_i\) 且 \(f_j\) 有值,此时 \(f_j\) 就会对 \(f_m\) 产生 \(f_j+k-i\) 的贡献,实时更新答案即可。\(j\) 的查找可以通过记录所有当前 \(f\) 有值的位置,\(\mathrm{lower\_bound}\) 一下即可。最后不要忘了多测清空。

#include<bits/stdc++.h>
using namespace std;
#define N 200010
int T,n,f[N];
struct rua
{
	int m,k;
	vector<int>d;
	void init(){m=k=0;d.clear();}
	void read(){
		d.clear();
		scanf("%d",&k);
		for(int i=1;i<=k;i++){
			int x;
			scanf("%d",&x);
			if(x<=m)continue;
			d.push_back(x);
			m=x;
		}
		k=d.size();
	}
	bool operator <(const rua &t)const{return m<t.m;}
}a[N];
vector<int>v;
void init()
{
	int ans=0;
	v.clear();
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		a[i].init();
		a[i].read();
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		for(int j=0;j<a[i].k;j++){
			int x=a[i].d[j];
			int k=lower_bound(v.begin(),v.end(),x)-v.begin()-1;
			f[a[i].m]=max(f[a[i].m],a[i].k-j);
			if(k>=0)f[a[i].m]=max(f[a[i].m],f[v[k]]+a[i].k-j);
			ans=max(ans,f[a[i].m]);
		}
		v.push_back(a[i].m);
	}
	for(auto i:v)f[i]=0;
	printf("%d\n",ans);
}
int main()
{
	scanf("%d",&T);
	while(T--)init();
}

1801D - The way home

题目大意:一个 \(n\) 个点 \(m\) 条边的有向图,每条边有对应花费。某个憨憨演唱家现在身上只有 \(p\) 元钱,他可以选择在某个点 \(i\) 开演唱会以得到 \(s_i\) 的收入。问要从 \(1\) 走到 \(n\) 所需要举办的演唱会次数最小是多少。

首先考虑如果回家的路径确定了,唱歌的决策是怎样的。可以发现最后肯定是在某个点 \(x\) 死命唱歌直到恰好攒够回家的钱,然后直接走最小花费的路径跑路。接着再倒着往回推,在这之前他肯定也是在另一个点恰好攒够钱后再动身来到 \(x\)。所以可以得到如果已经选定了先后在哪些位置开演唱会,那么其唱歌决策是确定的,即每次在当前位置一直举办演唱会直到攒够前往下一个位置的钱。

于是跑 \(n\) 遍单源最短路,对所有 \((i,j)\) 预处理出从点 \(i\) 走到点 \(j\) 的最小花费 \(dis_{i,j}\)。再使用类似最短路的过程贪心选取下个演唱会的地点。那么在这一部分的最短路中,有两个参考值,一个是 \(cost_i\) 表示到达 \(i\) 这个点需要举办演唱会的最小次数,另一个是 \(res_i\) 表示在次数最小的前提下,所剩余钱数的最大值。那么根据这两个信息跑最短路即可,每次到点 \(x\) 时,枚举下一个唱歌的位置 \(i\),根据当前的 \(res_x\) 和 \(dis_{x,i}\) 的差值以及在 \(x\) 举办演唱会的收入 \(s_x\) 可以得到需要举办演唱会的次数 \(k\),直接转移即可。最后答案就是 \(cost_n\)。

#include<bits/stdc++.h>
using namespace std;
#define N 810
#define LL long long
int T,n,m,p,b[N],vis[N];
LL dis[N],cost[N],res[N];
vector<pair<int,int>>d[N];
struct rua
{
	int x;
	LL cost,res;
	bool operator <(const rua &t)const{
		if(cost!=t.cost)return cost<t.cost;
		return res>t.res;
	}
};
set<rua>s;
void Dij(int x)
{
	set<pair<LL,int>>q;
	for(int i=0;i<=n;i++)dis[i]=1e18;
	dis[x]=0;
	for(int i=1;i<=n;i++)q.insert(make_pair(dis[i],i));
	while(!q.empty()){
		auto PI=*q.begin();
		q.erase(q.begin());
		int x=PI.second;
		for(auto pi:d[x]){
			LL nd=dis[x]+pi.second;
			int y=pi.first;
			if(nd<dis[y]){
				q.erase(make_pair(dis[y],y));
				dis[y]=nd;
				q.insert(make_pair(dis[y],y));
			}
		}
	}
}
LL ceil(LL x,LL y)
{
	if(x<0)return 0;
	if(x%y==0)return x/y;
	return x/y+1;
}
void init()
{
	s.clear();
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++){
		d[i].clear();
		scanf("%d",&b[i]);
		vis[i]=0;
		cost[i]=1e18;
		res[i]=0;
	}
	for(int i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		d[u].push_back(make_pair(v,w));
	}
	Dij(1);
	if(dis[n]==dis[0]){
		printf("-1\n");
		return;
	}
	cost[1]=0,res[1]=p;
	s.insert((rua){1,cost[1],res[1]});
	while(!s.empty()){
		auto X=*s.begin();
		s.erase(s.begin());
		int x=X.x;
		vis[x]=1;
		Dij(x);
		for(int i=1;i<=n;i++)if(!vis[i]){
			LL k=ceil(dis[i]-res[x],b[x]);
			rua tmp={i,cost[x]+k,res[x]+k*b[x]-dis[i]};
			rua I={i,cost[i],res[i]};
			if(tmp<I){
				s.erase(I);
				cost[i]=tmp.cost;
				res[i]=tmp.res;
				s.insert(tmp);
			}
		}
	}
	printf("%lld\n",cost[n]);
}
int main()
{
	scanf("%d",&T);
	while(T--)init();
}

赛场上为了省空间写了个类似于最短路套最短路的玩意orz

标签:1801A,857,int,Codeforces,物品,序列,oplus,maxA,演唱会
From: https://www.cnblogs.com/DeaphetS/p/17202190.html

相关文章

  • Codeforces Round 856 (Div. 2)
    Preface补题,话说这场题目数量好少的说……除了E题有点新花样前面题目都很简单的说,不过最后一天疯狂卡自然溢出的Hash,WA了一页可还行A.PrefixandSuffixArraySB题,我......
  • CodeForces 1789F Serval and Brain Power
    洛谷传送门CF传送门很牛逼的题啊!感觉套路很实用,感谢ntf。考虑\(totlen=cnt\timeslen\le80\)。若\(cnt\le3\),可以\(O(|S|^{2cnt-1})\)暴力枚分割点。\(c......
  • CF1780F Codeforces Round 846 (Div. 2) F. Three Chairs
    https://codeforces.com/contest/1780/problem/F计算\[\sum_{i,j,k}[gcd(min(a_i,a_j,a_k),max(a_i,a_j,a_k))=1]\]对\(a_i\)排序,则需计算\[\sum_{i<k......
  • Codeforces Round 855 (Div. 3) F
    F核心思路首先我们可以知道的是只要满足了条件2和条件3必然会满足条件1.因为奇数和奇数的乘积一定是奇数。这一个比较显而易见的性质。然后就是我们需要思考我们得使用......
  • Codeforces Round 855 (Div. 3)(E,F)
    CodeforcesRound855(Div.3)(E,F)在\(div2\)受虐久了,这次\(div3\)打的还蛮顺利的(虽然\(wa\)了好几次)EE题目大意就是有两个字符串,我们要通过以下两种操作把第一个字符变......
  • CF1789 Codeforces Round 853 (Div. 2) D. Serval and Shift-Shift-Shift
    https://codeforces.com/contest/1789/problem/D给定两个n位二进制数a,b,你可以每次使\(a=a\oplusa>>k\)或\(a=a\oplusa<<k\),你需要用不超过n次操作......
  • CF1793 Codeforces Round 852 (Div. 2) D. Moscow Gorillas
    https://codeforces.com/contest/1793/problem/D对于给定的两个长度为\(n\)的排列\(a_i,b_i\),定义\(MEX(S)\)为集合\(S\)中没有出现的最小的正整数,找出所有满足......
  • Codeforces Round 856 (Div. 2)
    《C.ScoringSubsequences》  这道题有很多解法:二分,双指针等,但是无论哪一种都要知道如下:想要得到当k时,最大的分数,那么就会贪心地将后面的数相乘再......
  • Codeforces Round 855 (Div
    Problem-E2-UnforgivableCurse(hardversion)给定一个初始字符串s和目标字符串t,我们可以对字符串s进行以下任意次操作:对于位置\(i\),如果\(i+k+1<=s.length()\)......
  • Codeforces Round 855 (Div. 3)
    链接CodeforcesRound855(Div.3)A题这个题先将大写变小写然后将重复元素去除,判断是不是等于meow就可以#include<iostream>#include<algorithm>#include<cstdi......