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;
}