title : 2022年桂林电子科技大学程序设计竞赛
tags : ACM,练习记录
date : 2022-5-29
author : Linno
2022年桂林电子科技大学程序设计竞赛(同步赛)
题目链接:https://ac.nowcoder.com/acm/contest/34773
小结:题都比较简单,比赛时大概做出10道大概是没问题的。
A-Overcooked!
支持①两个单点合并;②一个区间合并;③询问两个点是否在同一集合。
并查集搞一下就行了,第二个操作不需要用到数据结构,直接暴力跳祖先即可。
//#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=1e6+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,FA,fa[N],nxt[N],tr[N<<2];
inline int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void Solve(){
cin>>n>>m;
for(int i=1;i<=n;++i) fa[i]=i,nxt[i]=i+1; //nxt指向下一个没被融合的区域
for(int i=1,op,l,r,fx,fy;i<=m;++i){
cin>>op>>l>>r;
if(op==1){
fx=find(l);
fy=find(r);
fa[fx]=fy;
}else if(op==2){
fy=find(r);
for(int j=l;j<=r;){
fx=find(j);
fa[fx]=fy;
int tmp=j;
j=nxt[j];
nxt[tmp]=r+1;
}
}else{
if(find(l)==find(r)) cout<<"YES\n";
else cout<<"NO\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-It takes two
判断给定矩形是否由若干个边不重合的矩形A构建而成。
跑每一个连通块,记录上下左右范围,然后对这个范围的矩形判断是否点都在这个连通块内。
//#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=1007;
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');}
const int xx[]={0,0,1,-1},yy[]={1,-1,0,0};
int n,m,vis[N][N],U,D,R,L,idx=0;
char mp[N][N];
inline void dfs(int x,int y,int id){
if(x<1||x>n||y<1||y>m||mp[x][y]=='O') return;
vis[x][y]=id;
U=min(U,x);
D=max(D,x);
L=min(L,y);
R=max(R,y);
for(int d=0;d<4;++d){
int nx=x+xx[d],ny=y+yy[d];
if(!vis[nx][ny]) dfs(nx,ny,id);
}
}
void Solve(){
cin>>n>>m;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cin>>mp[i][j];
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(mp[i][j]=='A'&&!vis[i][j]){
U=L=inf;R=D=0;
dfs(i,j,++idx);
//cout<<U<<" "<<D<<" "<<L<<" "<<R<<"!!\n";
for(int k=U;k<=D;++k){
for(int l=L;l<=R;++l){
if(vis[k][l]!=idx){
cout<<"NO\n";
return;
}
}
}
}
}
}
cout<<"YES\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;
}
C-Don’t Starve
问能否将一棵树分成点权值和相等的三个部分。
从一个叶子出发遍历整棵树,找到权值为sum/3的就可以把这颗子树裁掉,一开始被这道题坑了,注意会存在满足的子树大于3个的情况。
//#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=2e6+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,sum,rt,flag,S[N],val[N],deg[N];
vector<int>G[N];
inline void dfs(int x,int f){
S[x]=val[x];
for(auto to:G[x]){
if(to==f) continue;
dfs(to,x);
S[x]+=S[to];
}
if(S[x]==sum){ //找到了一个子树满足条件
++flag;
S[x]=0; //清空子树
}
}
void Solve(){
cin>>n;
for(int i=1;i<=n;++i) cin>>val[i],sum+=val[i];
for(int i=1,u,v;i<n;++i){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
++deg[v];++deg[u];
}
if(sum%3){
cout<<"NO\n";
return;
}
sum/=3;
for(int i=1;i<=n;++i){
if(deg[i]==1){
rt=i;
break;
}
}
dfs(rt,0);
if(flag>=3) cout<<"YES\n";
else cout<<"NO\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;
}
D- Sid Meier’s Civilization
将序列划分成k段,每段子序列的价值是数字的种类数,问最大权值和。
O ( k ∗ n 2 ) O(k*n^2) O(k∗n2)相信大家都会,关键在于如何把状态转移的前驱结点裁一下。我们假设从j到i由一个开拓者看守,那么j就只由前一段中的所有数字第一次出现位决定。开个vector存一下有可能的前一段开拓者位置即可。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+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,ans=0,dp[N][55],a[N],cnt[N],stk[N],stq[N],top=0;
vector<int>vt;
void Solve(){
memset(cnt,0,sizeof(cnt));
n=read();K=read();
for(int i=1;i<=n;++i) a[i]=read();
vt.emplace_back(0);
for(int k=1,tmp=0;k<=K;++k){
for(int p=0,j;p<vt.size();++p){
j=vt[p];
for(int i=j+1;i<=n;++i){
if(cnt[a[i]]) stk[++top]=i-1,dp[i][k]=max(dp[i][k],dp[j][k-1]+tmp);
else cnt[a[i]]=1,stq[++tmp]=a[i],dp[i][k]=max(dp[i][k],dp[j][k-1]+tmp);
}
dp[n][k]=max(dp[n][k],dp[j][k-1]+tmp);
while(tmp) cnt[stq[tmp--]]=0;
}
vt.clear();
while(top) vt.emplace_back(stk[top--]);
stable_sort(vt.begin(),vt.end());
vt.erase(unique(vt.begin(),vt.end()),vt.end());
}
for(int j=1;j<=K;++j) ans=max(ans,dp[n][j]);
write(ans);
}
signed main(){
// 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-Ori and the Blind Forest
给定n个不相交区间的范围和m个跳跃距离,问能否从区间1跳到区间n。
将n个区间转化为n-1个需要的技能范围 [ l i , r i ] [l_i,r_i] [li,ri],然后我们按r从小到大排序,对跳跃距离从小到大排序,枚举需要的技能范围,将满足小于右端点的技能放入set离,二分得到第一个满足的技能匹配上即可。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int __int128
//#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 node{int l,r;}s[N];
bool cmp(node A,node B){return A.r<B.r;}
multiset<int>st;
int n,m,a[N],l[N],r[N],flag=0;
void Solve(){
n=read();m=read();
//cin>>n>>m;
for(int i=1;i<=m;++i) a[i]=read();//cin>>a[i];
for(int i=1;i<=n;++i){
//cin>>l[i]>>r[i];
l[i]=read();r[i]=read();
}
for(int i=1;i<n;++i){
s[i].l=l[i+1]-r[i];
s[i].r=r[i+1]-l[i];
}
sort(a+1,a+1+m);
sort(s+1,s+n,cmp);
int idx=1;
for(int i=1;i<n;++i){ //是否每个区间能匹配一个技能
while(idx<=m&&a[idx]<=s[i].r) st.insert(a[idx++]);
auto it=st.lower_bound(s[i].l);
if(it==st.end()){
flag=1;
break;
}
//cout<<(*it)<<" "<<i<<"!!\n";
st.erase(it);
}
if(flag) puts("NO");//cout<<"NO\n";
else puts("YES");//cout<<"YES\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;
}
F-XCOM
签到题,不中的概率乘起来即可。
void Solve(){
int n;
double ans=1.0,a;
cin>>n;
for(int i=1;i<=n;++i){
cin>>a;
ans*=(1.0-a);
}
printf("%.6lf",ans);
//cout<<ans<<"\n";
}
G-Devil May Cry
压轴题,给定序列a,求n-k+1个答案,每次求[i,i+k-1]中最长的严格上升子序列,并且要求选了 a i a_i ai之后必须选择第一个在区间内的比 a i a_i ai大的数。
每个点的下一个要选的数都是确定的,往后面连边,整个序列就变成了一棵树的形状。(这个过程可以用单调栈连边)。现在对每个点往后的LIS就是他在树上的深度了。
如何将范围限制在k之中?我们可以在树上求dfn序进行线段树操作,对于加入一个结点和删除一个结点,就是对其子树进行+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=2e6+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');}
vector<int>G[N];
inline void addedge(int u,int v){G[u].push_back(v);}
int n,k,val[N],top,stk[N],L[N],R[N],vis[N],idx=0;
inline void dfs(int x){
if(vis[x]) return;
vis[x]=1;
L[x]=++idx;
for(auto to:G[x]) dfs(to);
R[x]=idx;
}
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
int tr[N<<2],tg[N<<2];
inline void pushdown(int p,int l,int r){
if(l==r) return;
if(tg[p]){
tg[ls]+=tg[p];
tg[rs]+=tg[p];
tr[ls]+=tg[p];
tr[rs]+=tg[p];
tg[p]=0;
}
}
inline void upd(int p,int l,int r,int ql,int qr,int val){
if(ql<=l&&r<=qr){
tr[p]+=val;
tg[p]+=val;
return;
}
pushdown(p,l,r);
if(ql<=mid) upd(ls,l,mid,ql,qr,val);
if(qr>mid) upd(rs,mid+1,r,ql,qr,val);
tr[p]=max(tr[ls],tr[rs]);
}
void Solve(){
scanf("%d%d",&n,&k);
top=0;
for(int i=1;i<=n;++i){
scanf("%d",&val[i]);
while(top&&val[stk[top]]<val[i]){
addedge(i,stk[top]);
top--;
}
stk[++top]=i;
}
for(int i=n;i>=1;--i) if(!vis[i]) dfs(i);
for(int i=1;i<=k;++i) upd(1,1,n,L[i],R[i],1);
printf("%d ",tr[1]);
for(int i=k+1;i<=n;++i){
upd(1,1,n,L[i],R[i],1);
upd(1,1,n,L[i-k],R[i-k],-1);
printf("%d ",tr[1]);
}
}
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-Mental Omega
求以某个点为根最大的子树重量和。
显然以每个点开始求重量和累加是肯定是对的,但是会超。考虑钦定某个点为根,考虑不换根,有结点x朝儿子y的转移,其实就是 a n s [ y ] = a n s [ x ] + n − 2 ∗ s z [ t o ] ans[y]=ans[x]+n-2*sz[to] ans[y]=ans[x]+n−2∗sz[to]。
//#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');}
int n,m,rt,ans=0,dp[N],sz[N],fa[N],son[N],g[N];
vector<int>G[N];
inline void dfs1(int x,int f){
sz[x]=1;
for(auto to:G[x]){
if(to==f) continue;
// fa[to]=x;
dfs1(to,x);
sz[x]+=sz[to];
dp[x]+=dp[to];
}
dp[x]+=sz[x];
}
inline void dfs2(int x,int f){
for(auto to:G[x]){
if(to==f) continue;
g[to]=g[x]-dp[to]-2*sz[to]+n+dp[to];
}
for(auto to:G[x]){
if(to==f) continue;
dfs2(to,x);
}
}
void Solve(){
cin>>n;
for(int i=1,u,v;i<n;++i){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
// ++deg[u];++deg[v];
}
dfs1(1,0);
g[1]=dp[1];
dfs2(1,0);
for(int i=1;i<=n;++i){
// cout<<i<<" "<<dp[i]<<" "<<sz[i]<<" "<<g[i]<<"!!\n";
ans=max(ans,g[i]);
}
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;
}
I-Shadow Tactics
给定每个敌人的侦察范围和行走路径,问给定点会不会被敌人发现。
将每个敌人能侦察的范围变成一个矩形,然后判断点是否在矩形内即可。
//#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');}
int sx,sy;
bool check(int x1,int y1,int x2,int y2){
if(x1<=sx&&sx<=x2&&y1<=sy&&sy<=y2) return true;
return false;
}
void Solve(){
int n,R,x,y,w,x1,x2,y1,y2;
char c;
cin>>n>>R;
cin>>sx>>sy;
for(int i=1;i<=n;++i){
cin>>c>>x>>y>>w;
if(c=='U'){
x1=x-R,x2=x+R;y1=y-R;y2=y+w+R;
}else if(c=='D'){
x1=x-R,x2=x+R;y1=y-w-R;y2=y+R;
}else if(c=='L'){
x1=x-R-w,x2=x+R;y1=y-R;y2=y+R;
}else{
x1=x-R,x2=x+R+w;y1=y-R;y2=y+R;
}
if(check(x1,y1,x2,y2)){
cout<<"YES\n";
return;
}
}
cout<<"NO\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;
}
J-Sekiro:Shadows Die Twice
签到题,将字母替换然后输入输出即可。
void Solve(){
string str;
cin>>str;
mp['A']="RRRR";
mp['B']="RS";
mp['C']="RP";
mp['D']="L";
mp['E']="RRRRRRSL";
mp['F']="S";
mp['G']="PL";
for(int i=0;i<str.length();++i){
cout<<mp[str[i]];
}
}
K-Prototype
签到题,对一个数唯一分解,然后答案就是每一种质数的乘积。
int w,ans,np[N],pri[N],cnt=0;
void init(){
np[1]=1;
for(int i=2;i<N;++i){
if(!np[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&pri[j]*i<N;++j){
np[pri[j]*i]=1;
if(i%pri[j]==0) break;
}
}
}
void Solve(){
init();
cin>>w;
int ans=1;
for(int i=1;i<=cnt;++i){
if(w%pri[i]==0){
ans*=pri[i];
while(w%pri[i]==0) w/=pri[i];
}
}
if(w>1) ans*=w;
cout<<ans<<"\n";
}
L-Stardew Valley
签到题,找到比钱一个数小的,答案加上那个差值即可(等价于统一给后面的数加上那个差值)。
void Solve(){
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=2;i<=n;++i){
if(a[i]<a[i-1]){
ans+=a[i-1]-a[i];
}
}
cout<<ans<<"\n";
}