The 2022 ICPC Asia Hangzhou Regional Programming Contest

比赛连接:Dashboard - The 2022 ICPC Asia Hangzhou Regional Programming Contest - Codeforces

A. Modulo Ruins the Legend

题目大意:

给你一个长度为n的数组,确定s,d的值,对于数组第k个元素ak加上s+kd,k∈[1,n],使得加上后数组的和模m后值最小。

0<=s<m;0<=d<m;如果多解输出任意一个满足条件的即可。

n,m (1≤n≤10^5, 1≤m≤10^9).

思路:

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include<bitset>
#include<list> 
#include <algorithm>
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#define pdd pair<db,db>
#define se second 
#define fi first
#define endl '\n'
#define rep(i,a,b) for (register int i=a;i<b;++i)
#define per(i,a,b) for (register int i=a;i>b;--i)
#define MEM(a,x) memset(a,x,sizeof(a))
#define M(x) ((x)%MOD)
#define db double
#define eps 1e-9
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=9901;
const int N=25;
int gcd(int a,int b)
{
	if(!b) return a;
	return gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y)
{
	if(!b){
		x=1,y=0;
		return a;
	}
	int d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}
void solve()
{
	int n,m,x,y,sum=0;
	cin>>n>>m;
	rep(i,0,n){
		cin>>x;
		sum=(sum+x)%m;
	}
	int s,d;
	int g0=exgcd(n%m,1ll*n*(n+1)/2%m,s,d);
	if(sum==0||g0==0){
		cout<<0<<endl<<0<<" "<<0<<endl;
		return;
	}
	int g=g0,ans,t=sum;
	while(1){
		g=gcd(g,m);
		if(t%g==0){
			ans=0;
			break;
		}
		if(g>t){
			ans=t;
			break;
		}
		t%=g;
	}
	int T=((ans-sum)%m+m)%m;
	g=exgcd(g0,m,x,y);
	x=1ll*x*T/g%m;
	x=(x+m)%m;
	s=1ll*s*x%m,d=1ll*d*x%m;
	s=(s+m)%m,d=(d+m)%m;
	cout<<ans<<endl;
	cout<<s<<" "<<d<<endl;
}
int main()
{
//	#ifndef ONLINE_JUDGE
//		freopen("title.in","r",stdin);
//		freopen("title.out","w",stdout);
//	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	//cin>>_; 
	while(_--){
		solve();
	}
	return 0;
}

C. No Bug No Game

题目大意:

给定n个物品和限定k,要求最大化分数。物品的选择顺序可以任意。

第i物品一行pi代表个数,后面pi个wij代表容量(不一定递增),记录sum为∑pj (1≤j<i),对于第i个物品:

  • sum+pi≤k 得到w(i,pi)的分
  • sum≥k, 不能再加分了。
  • 否则得到w(i,k−sum)分。

1≤n≤3000, 0≤k≤3000

1≤pi≤10;1≤wi,j≤10^5

思路:

简单的背包问题变型,可以将sum看做是背包容量,在k之内做动态规划。区别就是如果sum+pi>k,就会有一个物品不选满(选到刚好达到k的容量并得到相应的价值)。

用f[i][j][z]表示从前i个物品当中选,总和刚好为j,且z=0表示所有物品选满,z=1表示有一个没有选满的物品。转移方程见代码。

最后在枚举更新答案的时候需要判断j==k的时候才可以更新f[i][j][1]这个情况。

代码: 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include<bitset>
#include<list> 
#include <algorithm>
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#define pdd pair<db,db>
#define se second 
#define fi first
#define endl '\n'
#define rep(i,a,b) for (register int i=a;i<b;++i)
#define per(i,a,b) for (register int i=a;i>b;--i)
#define MEM(a,x) memset(a,x,sizeof(a))
#define M(x) ((x)%MOD)
#define db double
#define eps 1e-9
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=9901;
const int N=3005;
int f[N][N][2],w[15];
void solve()
{
	int n,k,p;
	cin>>n>>k;
	MEM(f,-0x3f);
	f[0][0][0]=0;
	rep(i,1,n+1){
		cin>>p;
		rep(j,1,p+1) cin>>w[j];
		per(j,k-1,-1){
			f[i][j][0]=f[i-1][j][0];
		    f[i][j][1]=f[i-1][j][1];
		    if(j+p<=k) f[i][j+p][0]=max(f[i][j+p][0],f[i-1][j][0]+w[p]),f[i][j+p][1]=max(f[i][j+p][1],f[i-1][j][1]+w[p]);
		    rep(z,1,p) if(j+z<=k) f[i][j+z][1]=max(f[i][j+z][1],f[i-1][j][0]+w[z]);
		}
	}
	int ans=0;
	rep(i,1,n+1) rep(j,0,k+1){
		ans=max(ans,f[i][j][0]);
		if(j==k) ans=max(ans,f[i][j][1]);
	}
	cout<<ans;
}
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("title.in","r",stdin);
		freopen("title.out","w",stdout);
	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	//cin>>_; 
	while(_--){
		solve();
	}
	return 0;
}

D. Money Game

题目大意:

n个人玩游戏,第i个人初始值为ai;每一轮会进行:从前往后,前一个人把他的值一半给后一个人,最后一个会给第一个。

问经过2022^1204轮之后,输出每个人的值,误差在10^-6之内。

2≤n≤10^5

1≤ai≤10^6

思路:

打表可以看出经过很多轮次后,最后满足a1=2*a2=...=2*an,而且整个序列的和不会因为操作而改变。数值的和记为s,当次数趋于无穷时,第一个数为2s/(n+1),后面都是s/(n+1)。

代码: 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include<bitset>
#include<list> 
#include <algorithm>
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#define pdd pair<db,db>
#define se second 
#define fi first
#define endl '\n'
#define rep(i,a,b) for (register int i=a;i<b;++i)
#define per(i,a,b) for (register int i=a;i>b;--i)
#define MEM(a,x) memset(a,x,sizeof(a))
#define M(x) ((x)%MOD)
#define db double
#define eps 1e-9
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=9901;
const int N=25;
void solve()
{
	int n;
	db s=0;
	cin>>n;
	rep(i,0,n){
		db x;
		cin>>x;
		s+=x;
	}
	s/=(n+1);
	rep(i,0,n) if(i) printf("%.7lf ",s); else printf("%.7lf ",2*s);
}
int main()
{
//	#ifndef ONLINE_JUDGE
//		freopen("title.in","r",stdin);
//		freopen("title.out","w",stdout);
//	#endif
	//ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	//cin>>_; 
	while(_--){
		solve();
	}
	return 0;
}

E. Oscar is All You Need

题目大意:

T组测试,每组给定一个n,和1-n的排列,可以最多操作2*n+1次,每次操作将数组分为非空的三个部分,交换第一个和第三个部分。问字典序最小序列。输出操作次数和方案。(不要求操作次数最小)

1≤T≤120

3≤n≤1000

思路:

 

 

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include<bitset>
#include<list> 
#include <algorithm>
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#define pdd pair<db,db>
#define se second 
#define fi first
#define endl '\n'
#define rep(i,a,b) for (register int i=a;i<b;++i)
#define per(i,a,b) for (register int i=a;i>b;--i)
#define MEM(a,x) memset(a,x,sizeof(a))
#define M(x) ((x)%MOD)
#define db double
#define eps 1e-9
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=9901;
const int N=1010;
int n,a[N],b[N],sz;
pii p[2*N+5];
int got(int x)
{
	rep(i,1,n+1) if(a[i]==x) return i;
}
void op(int l,int r)
{
	p[++sz]={l-1,n-r};
	int cnt=0;
	rep(i,r+1,n+1) b[++cnt]=a[i];
	rep(i,l,r+1) b[++cnt]=a[i];
	rep(i,1,l) b[++cnt]=a[i];
	rep(i,1,n+1) a[i]=b[i];
}
void solve()
{
	cin>>n;
	rep(i,1,n+1) cin>>a[i];
	if(n==3){
		if(a[1]>a[3]) cout<<1<<endl<<1<<" "<<1<<endl;
		else cout<<0<<endl;
		return;
	}
	sz=0;
	int k=got(1);
	if(k!=1){
		if(k==2) op(3,3),op(n-1,n-1);
		else op(k-1,k-1);
	}
	rep(i,2,n-1){
		k=got(i);
		if(k==i) continue;
		if(k==i+1) op(i,i+1),op(n-i+1,n-i+1);
		else op(i,i),op(k-i,n-i+1);
	}
	if(a[n-1]!=n-1) op(n-2,n-1),op(2,2),op(2,n-1),op(n-1,n-1),op(2,3);
	cout<<sz<<endl;
	rep(i,1,sz+1) cout<<p[i].fi<<" "<<p[i].se<<endl;
}
int main()
{
//	#ifndef ONLINE_JUDGE
//		freopen("title.in","r",stdin);
//		freopen("title.out","w",stdout);
//	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_; 
	while(_--){
		solve();
	}
	return 0;
}

F. Da Mi Lao Shi Ai Kan De

题目大意:

有n各组,每组给一些字符串,从组中选择含有字串"bie"的加入到一组中(如果存在了不加了),如果可以添加就输出这个字符串,否则输出“Time to play Genshin Impact, Teacher Rice!”

思路:

水题,用string和set模拟即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include<bitset>
#include<list> 
#include <algorithm>
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#define pdd pair<db,db>
#define se second 
#define fi first
#define endl '\n'
#define rep(i,a,b) for (register int i=a;i<b;++i)
#define per(i,a,b) for (register int i=a;i>b;--i)
#define MEM(a,x) memset(a,x,sizeof(a))
#define M(x) ((x)%MOD)
#define db double
#define eps 1e-9
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=9901;
const int N=25;
set<string>st;
void solve()
{
	int n;
	bool f=0;
	cin>>n;
	while(n--){
		string s;
		cin>>s;
		if(s.find("bie")!=-1&&st.find(s)==st.end()){
			cout<<s<<endl;
			st.insert(s),f=1;
		}
	}
	if(!f) cout<<"Time to play Genshin Impact, Teacher Rice!"<<endl;
}
int main()
{
//	#ifndef ONLINE_JUDGE
//		freopen("title.in","r",stdin);
//		freopen("title.out","w",stdout);
//	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_; 
	while(_--){
		solve();
	}
	return 0;
}

K. Master of Both

题目大意:

给定n个字符串,q此询问,每一次询问都定义一个字母字典序大小规则,在这种大小规则下求n个字符串逆序对的个数。

1≤n≤5×10^5, 1≤q≤5×10^4

思路:

在比较两个字符串的时候,只需要比较第一个不相同字母的大小就可以了。建立trie树,在建树的过程中维护cnt[p]的值,表示以p为前缀的字符串个数。再用inv[i][j]记录序列中(j,i)的对数个数。

注意有一种特殊情况需要单独算:当一个字符串插入完后,要加上其所有的子节点。因为它可能是前面字符串的一个前缀,这种情况它一定会和这些字符串构成逆序对。

最后计算答案就是两重循环,每次加上预处理出来的inv[i][j] (j>i)即可。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include<bitset>
#include<list> 
#include <algorithm>
#define pii pair<int,int>
#define pll pair<LL,LL>
#define pil pair<int,LL>
#define pli pair<LL,int>
#define pdd pair<db,db>
#define se second 
#define fi first
#define endl '\n'
#define rep(i,a,b) for (register int i=a;i<b;++i)
#define per(i,a,b) for (register int i=a;i>b;--i)
#define MEM(a,x) memset(a,x,sizeof(a))
#define M(x) ((x)%MOD)
#define db double
#define eps 1e-9
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int MOD=1e9+7;
const int N=1e6+10;
int tr[N][26],idx=1;
LL tot,cnt[N],inv[26][26];
string s;
void ins()
{
	int p=1;
	++cnt[p];
	rep(i,0,s.size()){
		int j=s[i]-'a';
		rep(k,0,26) if(j!=k) inv[j][k]+=cnt[tr[p][k]];
		if(tr[p][j]==0) tr[p][j]=++idx;
		p=tr[p][j],++cnt[p];
	}
	rep(i,0,26) tot+=cnt[tr[p][i]];
}
void solve()
{
	int n,q;
	cin>>n>>q;
	while(n--){
		cin>>s;
		ins();
	}
	while(q--){
		LL ans=0;
		cin>>s;
		rep(i,0,s.size()) rep(j,i+1,s.size()) ans+=inv[s[i]-'a'][s[j]-'a'];
		cout<<ans+tot<<endl;
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
		freopen("title.in","r",stdin);
		freopen("title.out","w",stdout);
	#endif
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
	//cin>>_; 
	while(_--){
		solve();
	}
	return 0;
}

开坑开坑~~~

由于期末考试压力,其他题目待补题qwq,后面会持续更新~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值