【超好懂的比赛题解】第十九届同济大学程序设计竞赛暨高校网络友谊赛

57 篇文章 0 订阅
28 篇文章 0 订阅

title : 第十九届同济大学程序设计竞赛暨高校网络友谊赛
date : 2022-5-30
tags : ACM,题解,练习记录
author : Linno


第十九届同济大学程序设计竞赛暨高校网络友谊赛

题目链接:https://ac.nowcoder.com/acm/contest/34442

A-盒饭盲盒

直接打表(感觉大家都推了一段时间)得出式子: ( n − a ) 2 n 2 + a 2 + n a \frac{(n-a)^2}{n^2+a^2+na} n2+a2+na(na)2

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

inline int gcd(int a,int b){return b?gcd(b,a%b):a;}

void Solve(){
	int n,a;
	cin>>n>>a; 
	int fz=(n-a)*(n-a),fm=(n*n+a*a+n*a);
	int g=gcd(fz,fm);
	fz/=g;fm/=g;
	cout<<fz<<"/"<<fm<<"\n"; 
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

B-万历十五年

待补

C-攻城

阴间题,要特判一下如果n=1和n=0都是YES,一般情况只要记录所有堡垒需要的伤害数sum,sum%(6+n)==0并且最小值大于sum/(n+6)即可

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
bool cmp(int x,int y){return x>y;}

int n,h[N],sum,mi;

void Solve(){
	n=read();	
	sum=0;mi=inf;
	for(int i=1;i<=n;++i){
		h[i]=read();
		sum+=h[i];
		mi=min(h[i],mi);
	}
	if(n<=1){puts("YES");}
	else if(sum%(6+n)==0){
		if(mi>=sum/(n+6)) puts("YES");
		else puts("NO");
	}else puts("NO"); 
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	T=read();
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

D-两串糖果

dp转移方程 d p [ i ] = m a x { d p [ j − 1 ] + r e v [ j ] [ i ] } dp[i]=max\{dp[j-1]+rev[j][i]\} dp[i]=max{dp[j1]+rev[j][i]},我们可以用 O ( n 2 ) O(n^2) O(n2)获得rev[j][i]表示翻转[j,i]区间后的价值量,求法就是枚举区间长度然后枚举起点。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=5005;
const int mod=1e9+7;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

int n,a[N],b[N],c[N],res;
int ans[N][N],rev[N][N],dp[N];

void Solve(){
	//cin>>n;
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1;i<=n;++i){
		b[i]=read();
		rev[i][i]=a[i]*b[i];
	}
	for(int len=2;len<=n;++len){
		for(int i=1;i+len-1<=n;++i){
			int j=i+len-1;
			rev[i][j]=rev[i+1][j-1]+a[i]*b[j]+a[j]*b[i];
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=i;++j){
			dp[i]=max(dp[i],dp[j-1]+rev[j][i]); //翻转后面的区间 
		}
	} 
	write(dp[n]); 
	//cout<<dp[n]<<"\n";
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

E-只想要保底

题目形式很容易想到二分答案x,那么我们需要找到矩阵的某两行并起来使得每列都有大于x的那么我们考虑用一个优于 O ( n 2 ) O(n^2) O(n2)的方法来做,就可以想到bitset优化,记录前面每行的状态,查看map中是否有与当前行并起来满足条件的行即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
#define mk make_pair
#define pii pair<int,int>
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

int n,m,a[N][10],p1,p2;
int bi[N],tmp;
map<int,int>mp;
pii ans;

inline bool check(int x){
	mp.clear();
	pii res=mk(inf,inf);
	for(int i=1;i<=n;++i){
		bi[i]=0;
		for(int j=1;j<=m;++j){
			bi[i]=(bi[i]<<1)+(a[i][j]>=x);
		}
		if(!mp.count(bi[i]))mp[bi[i]]=i;
		for(int j=0;j<(1<<m);++j){
			if((bi[i]|j)==tmp&&mp.count(j)){
				p1=i,p2=mp[j];
				if(p1>p2) swap(p1,p2);
				res=min(res,mk(p1,p2));
			}
		}
	}
	if(res.first!=inf){
		ans=res;
		return true;
	}
	return false;
}

void Solve(){
	n=read();m=read();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			a[i][j]=read();
		}
	}
	for(int i=1;i<=m;++i) tmp=(tmp<<1)+1;
	int L=0,R=inf,M;
	while(R-L>1){
		M=(L+R)/2;
		if(check(M)) L=M;
		else R=M;
	}
	write(ans.first);putchar(' ');
	write(ans.second);putchar(' ');
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

F-捣乱的神

结论:如果n>=60时,一次合并就可以了。(可以自己动手构造试试)

其他情况下数据范围很小,暴力合并一段区间并且返回这个长度,检查是否满足题意即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

int n,a[N],s[N],ans=inf;

bool check(int l,int r){
	if(l==r) return false;
	for(int i=l;i<r;++i){
		if((s[i]^s[l-1])>(s[r]^s[i])) return true;
	}
	return false;
}

void Solve(){
	cin>>n;
	s[0]=0;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		s[i]=(s[i-1]^a[i]);
	}
	if(n>=90){  //最多异或一次 
		cout<<"1\n";
		return; 
	}
	for(int i=1;i<=n;++i){
		for(int j=i;j<=n;++j){
			if(check(i,j)){
				ans=min(ans,(j-i-1));
			}
		}
	}
	if(ans<inf) cout<<ans<<"\n";
	else cout<<-1<<"\n";
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

G-归零

对于小于等于k/2的数,考虑将其变为0,对于大于k/2的数,考虑将其变为k,那么这个过程就要求区间的数可以有序,很容易想到用主席树做。我们得到表示[l,r]的权值线段树后,按k/2划分两部分进行求和即可得到答案。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 1e9
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

struct Q{int l,r,k;}q[N];

struct node{
	int ls,rs,cnt,sum;
}tr[N<<5];
int idx=0;

int n,m,len,a[N],rt[N];

inline void update(int &p,int o,int l,int r,int val){
	p=++idx;
	tr[p]=tr[o];
	++tr[p].cnt;
	tr[p].sum+=val;
	if(l==r) return;
	int mid=((l+r)>>1);
	if(val<=mid) update(tr[p].ls,tr[o].ls,l,mid,val);
	else update(tr[p].rs,tr[o].rs,mid+1,r,val);
}

int S,T;

inline int query1(int p,int o,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr) return tr[p].sum-tr[o].sum;
	int mid=((l+r)>>1);
	int res=0;
	if(ql<=mid) res+=query1(tr[p].ls,tr[o].ls,l,mid,ql,qr);
	if(qr>mid) res+=query1(tr[p].rs,tr[o].rs,mid+1,r,ql,qr);
	return res;
}

inline int query2(int p,int o,int l,int r,int ql,int qr,int k){
	if(ql<=l&&r<=qr) return (tr[p].cnt-tr[o].cnt)*k-(tr[p].sum-tr[o].sum);
	int mid=((l+r)>>1);
	int res=0;
	if(ql<=mid) res+=query2(tr[p].ls,tr[o].ls,l,mid,ql,qr,k);
	if(qr>mid) res+=query2(tr[p].rs,tr[o].rs,mid+1,r,ql,qr,k);
	return res;
}

void Solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	for(int i=1;i<=m;++i){
		cin>>q[i].l>>q[i].r>>q[i].k;
	}
	for(int i=1;i<=n;++i){
		update(rt[i],rt[i-1],0,inf,a[i]);
	}
	for(int i=1;i<=m;++i){
		int ans=0;
		ans+=query1(rt[q[i].r],rt[q[i].l-1],0,inf,0,q[i].k/2);
		ans+=query2(rt[q[i].r],rt[q[i].l-1],0,inf,q[i].k/2+1,inf,q[i].k);
		cout<<ans<<"\n";
	}
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

H-打工人

比较裸的单调队列优化,状态转移方程 d p [ i ] = d p [ j ] + s [ j − > i ] + 2 dp[i]=dp[j]+s[j->i]+2 dp[i]=dp[j]+s[j>i]+2

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
#define pii pair<int,int>
#define mk make_pair
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

int n,K,W,p[N],w[N],s[N],dp[N];
pii q[N];

void Solve(){
	cin>>n>>K>>W;
	for(int i=1;i<=n;++i){
		cin>>p[i]>>w[i];
		s[i]=s[i-1];
		if(p[i]!=p[i-1]) ++s[i];
		w[i]+=w[i-1]; //前缀和 
	}
	int head=0,tail=-1;
	q[++tail]={0,-1};
	for(int i=1;i<=n;++i){
		while(head<=tail&&(i-q[head].first>K||w[i]-w[q[head].first]>W)) ++head;
		dp[i]=q[head].second+s[i]+2;
		pii fro={i,dp[i]-s[i+1]};
		while(head<=tail&&(q[tail].second>=fro.second)) --tail;
		q[++tail]=fro;
	}
	cout<<dp[n]<<"\n";
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

I-另类排序

不会,有空再补。

J-收集者

考虑以一个某个位置的0/1结尾对答案的贡献。1可以作为开头所以另外+1,有单个令的情况对整体贡献为1,对每个位置的答案都可以由前面0、1答案的前缀和进行转移。
d p [ 0 ] = d p [ 0 ] + d p [ 1 ] d p [ 1 ] = d p [ 0 ] + d p [ 1 ] + 1 dp[0]=dp[0]+dp[1]\\ dp[1]=dp[0]+dp[1]+1 dp[0]=dp[0]+dp[1]dp[1]=dp[0]+dp[1]+1

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

string str;
int tmp,dp[2];

void Solve(){
	cin>>str;
	int n=str.length();
	for(int i=0;i<n;++i){
		if(str[i]=='0'){
			tmp=1; //只有一个0的情况 
			dp[0]=(dp[0]+dp[1])%mod;
		}else dp[1]=(dp[1]+dp[0]+1)%mod;
	}
	cout<<(dp[0]+dp[1]+tmp)%mod;
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

K-乐观的R家族

签到题,统计然后输出答案即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
string str[N];
int n,m,a[N],cnt[10],ans=0;

void Solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>str[i];
	}
	for(int i=1;i<=m;++i) cin>>a[i];
	for(int i=1;i<=m;++i){
		memset(cnt,0,sizeof(cnt));
		int mx=0;
		for(int j=1;j<=n;++j){
			++cnt[str[j][i-1]-'A'];
		}
		for(int j=0;j<5;++j) if(cnt[j]>cnt[mx]) mx=j;
		ans+=cnt[mx]*a[i];
	//	cout<<mx<<" "<<cnt[mx]<<" "<<ans<<"!!\n";
	}
	cout<<ans<<"\n";
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;	return 0;
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值