2025icpc网络赛第一场The 2025 ICPC Asia East Continent Online Contest (I)
题目链接:The 2025 ICPC Asia East Continent Online Contest (I) - Dashboard - Contest - QOJ.ac
G. Sorting
思路
保证有所有的 a->a+1 就输出yes,赛时写dfs结果T了一发
代码
void solve(){int n,m;cin>>n>>m;vb vis(n+1,false);vis[1]=true;for(int i=1;i<=m;i++){int a,b;cin>>a>>b;if(b==a+1) vis[b]=true;}int x=1;while(x<=n&&vis[x]==true) x++;if(x==n+1){cout<<"Yes\n";}else{cout<<"No\n";}
}
B. Creating Chaos
思路
我们可以将的所有值看成一个二维的表格,我们只需要贪心地选出前k个行和列之和最大的即可,考虑如果抛弃i之后那么所有的关于i这一行和列的数需要都删除,我们要不断维护其值即可
补题的时候发现其实只需要输出连续的k个数即可(这是为什么呢?)
代码
#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,k;cin>>n>>k;vector<pll> res;for(int i=1;i<=n;i++){int x=0;for(int j=1;j<=n;j++){x+=__gcd(abs(i-j),n);}res.push_back({x,i});}vi ans(k+1);for(int i=1;i<=k;i++){sort(res.begin(),res.end());auto [v,id]=res.back();res.pop_back();ans[i]=id;for(int j=0;j<res.size();j++){res[j].first-=__gcd(abs(res[j].second-id),n);}}for(int i=1;i<=k;i++){cout<<ans[i]<<" \n"[i==k];}
}signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;// cin>>_;while(_--) solve();return 0;
}
I. Knapsack Problem
思路
把玩几个样例吧,能够得到从i到j走,和从j到i走所需的背包数量其实是一样的,那么我们就可以从T点出发跑一遍最短路即可,优先队列维护背包数量和背包剩余容量即可
代码
#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;struct node{int cnt,id,r; //数量,节点,剩余容量
};
struct cmp{bool operator()(const node &x,const node &y){if(x.cnt==y.cnt) return x.r<y.r;return x.cnt>y.cnt;}
};void solve(){int n,m,v,t;cin>>n>>m>>v>>t;vector<vector<pll>> e(n+1);for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;e[u].push_back({v,w});e[v].push_back({u,w});}vi dis(n+1,inf);auto dij=[&](int s)->void{vb vis(n+1,false);priority_queue<node,vector<node>,cmp> q;q.push({1,s,v});dis[s]=1;while(!q.empty()){auto [cnt,x,r]=q.top();q.pop();if(vis[x]) continue;vis[x]=true;for(auto [y,w]:e[x]){int ct,re;if(w>r) {ct=cnt+1;re=v-w;}else {ct=cnt;re=r-w;}if(dis[y]>=ct){dis[y]=ct;q.push({ct,y,re});}}}};dij(t);for(int i=1;i<=n;i++){if(dis[i]>=inf) cout<<"-1 ";else cout<<dis[i]<<" ";}cout<<"\n";
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;// cin>>_;while(_--) solve();return 0;
}
A. Who Can Win
思路
纯模拟倒是没什么难度,对于每个队伍我们只需要维护两个值,一个是封榜后所有提交都没有通过,二是封榜后的提交都通过了,最后遍历一遍队伍看能否成为冠军即可
代码
#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;struct node{string na;char tm;int tim;string zt;
};
bool cmp(const node&a,const node&b){return a.tim<b.tim;
}
void solve(){int n;cin>>n;vector<node>a(n+1);for(int i=1;i<=n;i++){cin>>a[i].na>>a[i].tm>>a[i].tim>>a[i].zt;}sort(a.begin()+1,a.end(),cmp);map<string,set<char>>ac;map<string,int>fs;map<string,map<char,int>>mp;map<string,set<char>>mtg;map<string,int>mfs;for(int i=1;i<=n;i++){string name =a[i].na;char tm =a[i].tm;int time =a[i].tim;string res =a[i].zt;if(ac[name].count(tm))continue;if(res=="Accepted"){fs[name] += time;ac[name].insert(tm);}else if(res=="Rejected"){mp[name][tm]++;}else{if(mtg[name].count(tm))continue;mtg[name].insert(tm);mfs[name]+=time;mfs[name]+= mp[name][tm]*20;}}for(auto[x,y]:ac){for(auto ele:y){if(mp[x].count(ele)){fs[x]+=mp[x][ele]*20;}}}int fashi=1e18;int tg=0;for(auto[x,y]:ac){int cnt=y.size();if(tg==cnt){fashi=min(fashi,fs[x]);}else if(cnt>tg){tg=cnt;fashi=fs[x];}}vector<string>ans;for(auto[x,y]:ac){if(tg==y.size()&&fashi==fs[x]){ans.push_back(x);continue;}int add = mtg[x].size();if(add+y.size()>tg){ans.push_back(x);}else if(add+y.size()==tg){int tot =fs[x]+mfs[x];if(tot<=fashi){ans.push_back(x);}}}sort(ans.begin(),ans.end());for(auto s:ans){cout<<s<<' ';}cout<<endl;
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;cin>>_;while(_--) solve();return 0;
}
M. Teleporter
思路
用dp来做,dp[i][j]表示节点i在用j次捷径的情况下到达1所用的最短时间
那么对于dp[i][0]来说可以最初跑一遍bfs来初始化一下
对于dp[i][j]的更新,假设一条捷径为[x,y]那么可以得到明确的两个更新
但只需要更新这两个节点的dp值吗?并不是,我们仍需要跑dfs来更新其余节点的dp值,因为某节点可能在经由某个或多个捷径后到达节点1距离变短了
所以对于使用k次捷径,我们需要用dfs1从上到下,dfs2从下到上来更新所有节点的dp[][k]值
最终统计答案即可
代码
#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;vector<vector<pll>> e(n+1);for(int i=1;i<n;i++){int u,v,w;cin>>u>>v>>w;e[u].push_back({v,w});e[v].push_back({u,w});}vector<pll> res(m+1);for(int i=1;i<=m;i++){cin>>res[i].first>>res[i].second;}vector<vi> dp(n+1,vi(n+1,inf));auto bfs=[&]()->void{dp[1][0]=0;queue<int> q;q.push(1);while(!q.empty()){int u=q.front();q.pop();for(auto [v,w]:e[u]){if(dp[v][0]!=inf) continue;dp[v][0]=dp[u][0]+w;q.push(v);}}};bfs();auto dfs1=[&](auto self,int u,int i,int fa)->void{for(auto [v,w]:e[u]){if(v==fa) continue;self(self,v,i,u);dp[u][i]=min(dp[u][i],dp[v][i]+w);}};auto dfs2=[&](auto self,int u,int i,int fa)->void{for(auto [v,w]:e[u]){if(v==fa) continue;dp[v][i]=min(dp[v][i],dp[u][i]+w);self(self,v,i,u);}};for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){auto [x,y]=res[j];dp[x][i]=min(dp[x][i],dp[y][i-1]);dp[y][i]=min(dp[y][i],dp[x][i-1]);}dfs1(dfs1,1,i,0);dfs2(dfs2,1,i,0);}//将dp[i][k]的意义变成不超过k次的最短距离for(int i=1;i<=n;i++){for(int k=1;k<=n;k++){dp[i][k]=min(dp[i][k],dp[i][k-1]);}}for(int k=0;k<=n;k++){int ans=0;for(int i=1;i<=n;i++){ans+=dp[i][k];}cout<<ans<<"\n";}
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;// cin>>_;while(_--) solve();return 0;
}
D. Min-Max Tree
思路
-
树形动态规划(DP):对于每个节点,我们维护三个状态:
-
dp[u][0]
:表示以节点u为根的子树中,经过u的路径贡献之和的最大值。 -
dp[u][1]
:表示以节点u为根的子树中,u作为路径最小值时的最大贡献。 -
dp[u][2]
:表示以节点u为根的子树中,u作为路径最大值时的最大贡献。
-
-
状态转移:对于每个节点u,计算所有子节点v的
dp[v][0]
之和sum
。然后计算up
和down
:-
up
:u作为最大值时,向下延伸路径的最大贡献,通过调整子节点v的贡献和权重差得到。 -
down
:u作为最小值时,向下延伸路径的最大贡献,同样通过调整子节点v的贡献和权重差得到。
-
-
组合贡献:节点u作为中间点时,组合向上和向下的路径贡献,得到最终的总贡献。
代码
#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;cin>>n;vector<int> a(n+1);for(int i=1;i<=n;i++){cin>>a[i];}vector<vector<int>> e(n+1);for(int i=1;i<n;i++){int u,v;cin>>u>>v;e[u].push_back(v);e[v].push_back(u);}vector<array<int,3>> dp(n+1);auto dfs=[&](auto self,int u,int fa)->void{int sum=0;for(auto v:e[u]){if(v==fa) continue;self(self,v,u);sum+=dp[v][0];}int up=sum,down=sum;for(auto v:e[u]){if(v==fa) continue;up=max(up,sum-dp[v][0]+dp[v][2]+a[u]-a[v]);down=max(down,sum-dp[v][0]+dp[v][1]+a[v]-a[u]);}dp[u][0]=up+down-sum;dp[u][1]=down;dp[u][2]=up;};dfs(dfs,1,0);cout<<dp[1][0]<<"\n";
}
signed main() {vcoistntcout<<fixed<<setprecision(2);int _=1;// cin>>_;while(_--) solve();return 0;
}