2020ICPC 昆明站 个人题解

本文提供2020ICPC昆明站部分题目的详细解答,包括H题HardCalculation的简洁算法、J题ParallelSort中通过转换环结构优化排序次数的方法、K题Riichi!!中实现麻将胡牌判断的策略以及L题Simone and graph coloring中应用动态规划求解最长下降子序列问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title : 2020ICPC 昆明站 个人题解
date : 2022-11-21
tags : ACM,题解,练习记录
author : Linno


2020ICPC 昆明站 个人题解

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

补题进度:5/13

H-Hard Calculation

#include<bits/stdc++.h>
#define int long long
using namespace std;

void solve(){
	int n;
	cin>>n;
	cout<<n+2020<<"\n"; 
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T=1;
//	cin>>T;
	while(T--){
		solve();
	} 
	return 0;
}

J-Parallel Sort

我们可以把序列里面的环全部拉出来,并且对于一个环而言,展开成链,第一个和最后一个交换,第二个和倒数第二个交换,直到中间,这样一次操作可以保证把所有环都变成二元环,也因此操作最多不会超过2次。

#include<bits/stdc++.h>
//#define int long long
#define pii pair<int,int>
#define mk make_pair
#define F first
#define S second
using namespace std;
const int N=1e5+7;

int n,a[N],vis[N],mx,num,flag,idx;
vector<vector<int>>vt;
vector<int>tmp;
vector<vector<pii>>ans;
vector<pii>tmpp;

void dfs(int x) {
	vis[x]=1;
	tmp.emplace_back(x);
	if(vis[a[x]]) return;
	dfs(a[x]);
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1; i<=n; ++i) cin>>a[i];
	num=0;
	while(1){
		flag=1,idx=0;mx=0;
		for(int i=1;i<=n;++i) vis[i]=0;
		for(int i=1;i<=n;++i){
			if(a[i]!=i&&!vis[i]){
				tmp.clear();
				flag=0;
				++idx;
				dfs(i);  //把环全部拉出来
				vt.emplace_back(tmp);
			}
		}
		if(flag) break;
		++num;
		tmpp.clear();
		for(int i=0;i<idx;++i){
			int len=vt[i].size(),k=len/2;
			if(len==2){
				int x=vt[i][0],y=vt[i][1];
				tmpp.emplace_back(mk(x,y));
				swap(a[x],a[y]);
				continue;
			}
			for(int l=1,r=len-1;l<r;++l,--r){
				int x=vt[i][l],y=vt[i][r];
				tmpp.emplace_back(mk(x,y));
				swap(a[x],a[y]);
			}
		}
		ans.emplace_back(tmpp);
		vt.clear();
		//for(int i=1;i<=n;++i) cout<<a[i]<<" \n"[i==n];
	}
	cout<<num<<"\n";
	for(int i=0; i<num; ++i) {
		cout<<ans[i].size()<<" ";
		for(auto p:ans[i]){
			if(p.F>p.S) swap(p.F,p.S);
			cout<<p.F<<" "<<p.S<<" ";
		}
		cout<<"\n";
	}
	return 0;
}
/*
11
2 3 4 5 6 7 8 9 10 11 1

*/

K-Riichi!!

特判一开始胡的状态。先枚举要扔的牌,然后枚举摸的牌(这里根据听牌条件剪掉一些选择),然后再枚举对子,check一下是否胡就行了。同时要优化细节。

#pragma GCC optimize("Ofast") 
#include<cstdio>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;

string str;
int n,num[55],cnt[55];

string ss[10][10]={
{"1w","2w","3w","4w","5w","6w","7w","8w","9w"},
{"1b","2b","3b","4b","5b","6b","7b","8b","9b"},
{"1s","2s","3s","4s","5s","6s","7s","8s","9s"},
{"1z","2z","3z","4z","5z","6z","7z"}
};

string sd(int x){
	int p=x/9,q=x%9;
	return ss[p][q];
}

int id(char x,char y){
	int res;
	if(y=='w') res=0;
	else if(y=='b') res=1;
	else if(y=='s') res=2;
	else if(y=='z') res=3;
	return res*9+(x-'1');
}

void check2(){
	for(int i=0,x,y,z,k;i<=2;++i){
		for(int j=0;j+2<9;++j){
			x=i*9+j,y=x+1,z=x+2;
			while(cnt[x]%3!=0){
				cnt[x]--,cnt[y]--,cnt[z]--;	
			}
		}
	}
	for(int i=0;i<=33;++i) if(cnt[i]>=3) cnt[i]-=3; //去掉所有kezi 
}

bool check1(){
	for(int i=0;i<=33;++i){
		if(num[i]>=2){
			for(int k=0;k<=33;++k) cnt[k]=num[k];
			cnt[i]-=2;
			check2(); 
			int flag=1;
			for(int k=0;k<=33;++k) if(cnt[k]) flag=0;
			if(flag) return true;
		}
	}
	return false;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int t=1;t<=n;++t){
		cin>>str;
		for(int i=0;i<=33;++i) num[i]=0;
		for(int i=0;i<28;i+=2){
			++num[id(str[i],str[i+1])];
		}
		if(check1()){  //是否达到胡的状态 
			cout<<"Tsumo!\n";
			continue;
		}
		vector<pair<string,string>>ans;
		for(int i=0;i<=33;++i){ //表示要扔掉的牌,这里只有14种情况 
			if(!num[i]) continue;
			--num[i];
			string tmp="";
			for(int j=0;j<=33;++j){ //剪掉没有或者附近没有的牌 
				if(j==i||(!num[j]&&!(j>=1&&num[j-1])&&!num[j+1])) continue;
				++num[j];
				if(check1()) tmp+=sd(j);
				--num[j];
			}
			if(tmp.size()) ans.emplace_back(make_pair(sd(i),tmp));
			++num[i];
		}
		cout<<ans.size()<<"\n";
		for(auto p:ans){
			cout<<p.first<<" "<<p.second<<"\n";			
		}
	}
	return 0;
}

L-Simone and graph coloring

最多用的颜色数即为最大团数,在这道题里其实就相当于求每个位置结尾的最长下降子序列,然后根据DP基础知识,排列可以稀疏性优化,就能 O ( n l o g n ) O(nlogn) O(nlogn)过了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+7;

int a[N],b[N],dp[N],ans[N]; 

void solve(){
	int n;
	cin>>n;
	int mxlen=1;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		a[i]=-a[i]; //变成相反数就等于求最长下降子序列了 
		int p=lower_bound(b+1,b+1+mxlen,a[i])-b-1; 
		dp[i]=p+1;
		b[dp[i]]=a[i];
		mxlen=max(mxlen,p+1);
	}
	cout<<mxlen<<"\n"; //最大颜色数=最大团数
	for(int i=1;i<=n;++i) cout<<dp[i]<<" \n"[i==n];
	for(int i=1;i<=n;++i) a[i]=b[i]=dp[i]=ans[i]=0;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T=1;
	cin>>T;
	while(T--){
		solve();
	} 
	return 0;
}

M-Stone Games

主席树,每次询问求一个ans=x使得前x+1个位置的数×个数的总和是大于上一次x的。

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e6+3;

struct T{
	int ls,rs;
	long long sum;	
}tr[N*40]; 

long long rt[N],n,m,cre=0,a[N],maxi;
#define mid ((l+r)>>1)

int insert(int o,long long l,long long r,long long pos,long long val){
	int p;
	p=++cre;
	tr[p]=tr[o];
	if(l==r) {
		tr[p].sum+=val*pos;
		return p;	
	}
	if(pos<=mid) tr[p].ls=insert(tr[p].ls,l,mid,pos,val);
	else tr[p].rs=insert(tr[p].rs,mid+1,r,pos,val);
	tr[p].sum=tr[tr[p].ls].sum+tr[tr[p].rs].sum;
	return p;
}

long long query(int p,int o,long long l,long long r,long long L,long long R){
	if(l>r||l>R) return 0;
	if(L<=l&&r<=R) return tr[p].sum-tr[o].sum;
	long long sum=0;
	if(L<=mid) sum+=query(tr[p].ls,tr[o].ls,l,mid,L,R);
	if(R>mid) sum+=query(tr[p].rs,tr[o].rs,mid+1,r,L,R);
	return sum;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
	cin>>n>>m;
	maxi=1; 
	for (int i=1;i<=n;++i) {
		cin>>a[i];
		maxi=max(a[i],maxi);
	}
	for (int i=1;i<=n;++i){
		rt[i]=insert(rt[i-1],1,maxi,a[i],1);
	} 
//	cout<<query(rt[3],rt[0],1,maxi,1,5)<<"\n";
	for(long long i=1,lp,rp,l,r,lst=0,x;i<=m;++i){
		cin>>lp>>rp;
		l=min((lp+lst)%n+1,(rp+lst)%n+1);
		r=max((lp+lst)%n+1,(rp+lst)%n+1);
		x=0,lst=query(rt[r],rt[l-1],1,maxi,1,x+1);
	//	cout<<l<<" "<<r<<" "<<lst<<"!!\n";
		while(lst>x){
			x=lst;
			lst=query(rt[r],rt[l-1],1,maxi,1,x+1);
		//	cout<<x<<" "<<lst<<"!!\n";	
		}
		++lst;
		cout<<lst<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

RWLinno

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

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

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

打赏作者

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

抵扣说明:

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

余额充值