2024 ICPC 沈阳(JDBEM)
The 2024 ICPC Asia Shenyang Regional Contest
题目链接:The 3rd Universal Cup. Stage 19: Shenyang - 比赛主页 - 比赛 - QOJ.ac
J. Make Them Believe
思路
签到
代码
void solve(){vector<pair<int,string>> x(4);vector<pair<int,string>> y(4);for(int i=0;i<4;i++) cin>>x[i].second>>x[i].first;for(int i=0;i<4;i++) cin>>y[i].second>>y[i].first;sort(x.begin(),x.end());sort(y.begin(),y.end());if(x[3].first>y[3].first){cout<<x[3].second<<" beats "<<y[3].second<<"\n";}else{cout<<y[3].second<<" beats "<<x[3].second<<"\n";}}
D. Dot Product Game
思路
这就不得不说了,vp的时候看了一眼就做出来了(唯一不多的高光时刻)
设A:B:
只有
且
才能交换a,只有
且
才能交换b,发现交换之后逆序对会减少1,然后便得出如果两者之间逆序对如果奇偶性相同则B赢,如果奇偶性不同则A赢。
再来看一下操作,首先我们要知道一点,每交换两个相邻的数那么逆序对数量就会改变1,那么放到问题中的左移操作呢?每左移一次就相当于将头部的数不断交换到尾部,那么左移d次奇偶性改变便是((r-l)*d)%2
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;const int N=2e5+10;
const int inf=1e18;
const int mod=998244353;#define lowbit(x) ((x)&-(x))
struct BIT{int n;vector<int> tree;void init(int x){n=x;tree.resize(x+10);}void update(int x,int d){for(int i=x;i<=n;i+=lowbit(i)){tree[i]+=d;}}int sum(int x){int ans=0;for(int i=x;i>0;i-=lowbit(i)){ans=ans+tree[i];}return ans;}
};void solve(){int n;cin>>n;vi a(n+1);vi b(n+1);//统计逆序对的数量BIT bta;bta.init(n);BIT btb;btb.init(n);int cta=0,ctb=0;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){cin>>b[i];}for(int i=n;i>=1;i--){cta+=bta.sum(a[i]);bta.update(a[i],1);ctb+=btb.sum(b[i]);btb.update(b[i],1);}int fa=(cta%2);int fb=(ctb%2);int f=(fa+fb)%2;string ans="";if(f%2) ans+="A";else ans+="B";for(int i=1;i<n;i++){char t;int l,r,d;cin>>t>>l>>r>>d;int x=d*(r-l);if(t=='A') fa=(fa+x)%2;else fb=(fb+x)%2;f=(fa+fb)%2;if(f%2) ans+="A";else ans+="B";}cout<<ans<<"\n";
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;while(_--) solve();return 0;
}
B. Magical Palette
思路
构造题当然要先打表了,可以尝试跑一下n与m都很小的时候,发现当n与m不互质的时候是无解的
然后就可以看一些跑出来的数据来猜测一下了(----)
数组a可以构造成,b构造成
,两者相乘可得
要取余n*m所以得到
这样构造就能够实现当i与j不同是取余出来的值也不同
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;const int N=2e5+10;
const int inf=1e18;
const int mod=998244353;void solve(){int n,m;cin>>n>>m;vi a(n+1);vi b(m+1);if(n==1&&m==1){cout<<"Yes\n";cout<<"0\n0\n";return;}if(n==1||m==1){if(n==1){a[1]=1;for(int i=1;i<=m;i++) b[i]=i-1;}if(m==1){b[1]=1;for(int i=1;i<=n;i++) a[i]=i-1;}cout<<"Yes\n";for(int i=1;i<=n;i++){cout<<a[i]<<" ";}cout<<"\n";for(int i=1;i<=m;i++){cout<<b[i]<<" ";}cout<<"\n";return;}int g=__gcd(n,m);if(g!=1){cout<<"No\n";return;}for(int i=1;i<=n;i++) a[i]=1+(i-1)*m;for(int i=1;i<=m;i++) b[i]=1+(i-1)*n;cout<<"Yes\n";for(int i=1;i<=n;i++){cout<<a[i]<<" ";}cout<<"\n";for(int i=1;i<=m;i++){cout<<b[i]<<" ";}cout<<"\n";
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;while(_--) solve();return 0;
}
E. Light Up the Grid
思路
TSP问题,一个经典的状压dp的题
对于2x2的网格所有情况可以将其表示成0~15的数值,如
1 | 0 |
0 | 1 |
则可以表示成1*1+0*2+0*4+1*8=9,那么进而我们可以用一个由16个二进制位来表示集合的状态即,例如
则表示集合由
0 | 1 |
0 | 0 |
0 | 0 | |
0 | 1 |
这两个方格组成
由于在方格状态达到15的时候就发出了提示音,也就是说在之后的操作中就可以删去此方格了,我们不妨从15状态出发,随机经过所有集合中出现过的方格看其最短代价即可
那么问题便转化成了经典问题了
设其中ms表示集合的状态(0~2^16-1),i表示最后经过的状态为i
那么状态转移方程便是:
其中为从j出发到达i的上一个状态,dis[j][i]表示从j状态到i状态的最短距离
dis由题意可以由最短路Floyd求得
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;const int N=2e5+10;
const int inf=1e18;
const int mod=998244353;vector<vector<int>> dis(16,vector<int>(16,inf));
vector<vector<int>> dp((1<<16),vector<int>(16,inf));int x[9]={1,2,4,8,3,12,5,10,15};
int a0,a1,a2,a3;
void init(){int v[9]={a0,a0,a0,a0,a1,a1,a2,a2,a3};for(int i=0;i<16;i++){if(i!=15) dis[i][i]=0;else dis[i][i]=min({a0,a1,a2,a3})*2;for(int j=0;j<9;j++){dis[i][i^x[j]]=v[j];}}for(int k=0;k<16;k++){for(int i=0;i<16;i++){for(int j=0;j<16;j++){dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}}}for(int i=0;i<16;i++) dp[(1<<i)][i]=dis[15][i];for(int ms=0;ms<(1<<16);ms++){for(int i=0;i<16;i++){if((1<<i)&ms){for(int j=0;j<16;j++){dp[ms][i]=min(dp[ms][i],dp[ms^(1<<i)][j]+dis[j][i]);}}}}
}void solve(){int n;cin>>n;int now=0;for(int i=1;i<=n;i++){char a,b,c,d;cin>>a>>b>>c>>d;int t=(a-'0')+(b-'0')*2+(c-'0')*4+(d-'0')*8;now+=(1ll<<t);}int ans=inf;for(int i=0;i<16;i++){ans=min(ans,dp[now][i]);}cout<<ans<<"\n";}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;cin>>a0>>a1>>a2>>a3;init();while(_--) solve();return 0;
}
M. Obliviate, Then Reincarnate
思路
强连通分量的题,Tarjan
首先要转化题意,要对 a%n--->(a+b)%n 建权值为b的边,对于每个x%n来说看其是否能够到达非零的环,这样就能使得x不断加大,进而成为无限
对于非零环的判断我们只需要从环上任意点出发走一圈看权值和是否为0即可
代码
#include<bits/stdc++.h>
using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
#define int long long
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;const int N=5e5+10;int n,m,q;
vector<vector<pll>> e(N); //存储未缩点的边集
vector<vector<int>> g(N); //存储缩点后的边集//Tarjan板子
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],cnt;
void tarjan(int x){dfn[x]=low[x]=++tot;stk[++top]=x,instk[x]=1;for(auto [y,w]:e[x]){if(!dfn[y]){tarjan(y);low[x]=min(low[x],low[y]);}else if(instk[y])low[x]=min(low[x],dfn[y]);}if(dfn[x]==low[x]){int y;++cnt;do{y=stk[top--];instk[y]=0;scc[y]=cnt;}while(y!=x);}
}vector<bool> vis(N); //记录已访问点
vector<int> dis(N); //记录跑完一圈后权值和是否为0
bool flag=false;
void dfs(int x,int col){ //看是否形成非0环,col为判断tarjan缩点是否同一个环vis[x]=true;for(auto [y,w]:e[x]){if(scc[y]!=col) continue;if(vis[y]){if(dis[y]!=dis[x]+w) flag=true;}else{dis[y]=dis[x]+w;dfs(y,col);}}
}void solve(){cin>>n>>m>>q;//建边for(int i=1;i<=m;i++){int a,b;cin>>a>>b;e[((a%n+n)%n)].push_back({((a+b)%n+n)%n,b});}//Tarjan缩点for(int i=0;i<n;i++) if(!dfn[i]) tarjan(i);queue<int> que; //遍历所有能够到达非0环缩点的集合vector<bool> bvis(n); //记录所有能够到达非0环缩点的集合vector<bool> vis1(n); //判断缩点是否被搜索过for(int i=0;i<n;i++){ //对每个缩点进行搜索,看其是否为非0环if(vis1[scc[i]]) continue;vis1[scc[i]]=true;flag=false;dfs(i,scc[i]);if(flag){bvis[scc[i]]=true;que.push(scc[i]);}}for(int i=0;i<n;i++){ //建立缩点后的边for(auto [j,w]:e[i]){if(scc[i]==scc[j]) continue;g[scc[j]].push_back(scc[i]);}}while(!que.empty()){ //进一步标记所有能进入非0环的点int x=que.front();que.pop();for(auto y:g[x]){if(bvis[y]) continue;bvis[y]=true;que.push(y);}}while(q--){ //输出答案int x;cin>>x;x=(x%n+n)%n;if(bvis[scc[x]]) cout<<"Yes\n";else cout<<"No\n";}}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;// cin>>_;while(_--) solve();return 0;
}