前言:
教练突然发在群里的一个比赛,想到自己好像也没什么事干,就参加了个div2(太菜了),我看着好像没啥人发这玩意的题解,cf上比赛结束了也看不了别人代码,就将自己的写出题的代码分享出来,剩下没写过的之后再补吧。
正文:
比赛链接:Dashboard - 2024 Xiangtan University Summer Camp-Div.2 - Codeforces
题目:
A. 二度树上的染色游戏:
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int a[N],h[N],e[N],ne[N],cnt[N],idx;
void add(int a,int b){
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
int ans=999999999;
void bfs(){
queue<pair<int,int> >q;
q.push({1,a[1]});
while(!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop();
//cout<<x<<" "<<y<<endl;
if(cnt[x]<2){
ans=min(ans,y);
}
//cout<<ans<<endl;
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
//cout<<j<<endl;
q.push({j,y+a[j]});
}
}
}
int main(){
memset(h,-1,sizeof(h));
int n,tot=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
tot+=a[i];
}
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
add(u,v);
cnt[u]++;
}
bfs();
cout<<tot-2*ans<<endl;
return 0;
}
通过bfs来找一条路径来表示被染成红色的部分,这条路径是从根节点到一个没有或子节点只有一个的点,通过bfs找到所有满足的路径并更新答案即可。
C. gcd hard version:
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
typedef long long ll;
int n,a[N],b[N],dp[N][100];
ll pre[N];
void init(){
for(int i=1;i<=n;i++){
dp[i][0]=a[i];
}
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
dp[i][j]=__gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int query(int l,int r){
if(l==r)return dp[l][0];
int j=log2(r-l+1);
return __gcd(dp[l][j],dp[r-(1<<j)+1][j]);
}
int main(){
ll ans=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cin>>b[i];
pre[i]=pre[i-1]+b[i];
//if(b[i]>=a[i])ans+=(n-i+1);
}
init();
for(int l=1;l<=n;l++){
int low=l,high=n+1,mid;
while(low<high){
mid=(low+high)>>1;
int res=query(l,mid);
ll sum=pre[mid]-pre[l-1];
//cout<<mid<<" "<<res<<" "<<sum<<endl;
if(res<=sum){
high=mid;
}
else low=mid+1;
//cout<<" "<<high<<" "<<l<<endl;
}
//cout<<high<<" "<<l<<endl;
ans+=(n-high+1);
}
cout<<ans<<endl;
return 0;
}
先通过ST表预处理出区间的gcd值,用前缀和来求sum值,因为随着区间的增大,gcd只会减小或不变,sum只会变大,所以可以二分求解。枚举l(左端点),用low和high来二分求解出第一个满足左端点l且gcd<sum的右端点,右端点的所有点都可以和该左端点构成一个合理的区间,这边注意如果正常二分如果没有满足的右端点他会找到n这个点,为了防止与n点刚好是第一个右端点二分时将high设为n+1。
D. 战至终章:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;ll n,p,ans;
int a[N],b[N],r[N];
int h[2*N],e[2*N],ne[2*N],idx;
void add(int a,int b){//让a指向b
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
struct node{
int x;
int y;
};
bool operator < (const node &a,const node &b){
return a.y>b.y;
}
priority_queue<node> pq;
priority_queue<int,vector<int>,greater<int> > ans2;
void tuopu(){
for(int i=1;i<=n;i++){
if(r[i]==0){
pq.push({i,a[i]});
}
}
while(!pq.empty()){
node res=pq.top();
pq.pop();
// cout<<res.x<<" "<<res.y<<endl;
if(p>=res.y){
p+=b[res.x];
ans++;
ans2.push(res.x);
}
else {
break;
}
for(int i=h[res.x];i!=-1;i=ne[i]){
int z=e[i];
//cout<<z<<endl;
r[z]--;
if(r[z]==0)pq.push({z,a[z]});
}
}
}
int main(){
memset(h,-1,sizeof(h));
cin>>n>>p;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++){
int k;
cin>>k;r[i]=k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
add(x,i);
}
}
tuopu();
cout<<ans<<endl;
while(!ans2.empty()){
cout<<ans2.top()<<" ";
ans2.pop();
}
return 0;
}
和官方题解的说法一样(官方题解没代码)见下:
由于b[i]>0所以杀魔神一定有利的。也就是说能杀就杀。 进一步发现这个问题本质上是在用小根堆跑拓扑排序。
待补:
B. 小文的排列:
E. LOL:
F. 810975:
后记:
虽然是简单的那套题,对我来说也是很难的啊,而且这比赛结束了不能看别人代码(自己没过的题),官方题解也没发代码,导致我这个菜鸡没法发现自己的错误,很多题目思路对了但还是改了很久,还是需要多练啊!