Codeforces Round #407 (Div. 1)

    技术2024-04-13  83

    Codeforces Round #407 (Div. 1)


    A. Functions again

    题意:

    给定长度为n的数组a 区间[L,R]的权值 f 为: 求所有区间中最大的 f 值

    数据范围:n<=1e5

    解法:

    令b[i]=abs(a[i]-a[i+1]) 那么答案一定在{b[1],-b[2],b[3],-b[4]…}或者{b[2],-b[3],b[4]…}中 对两者计算最大子段和取max就是答案

    code:

    #include<bits/stdc++.h> using namespace std; #define int long long const int maxm=1e5+5; int a[maxm]; int b[maxm]; int n; signed main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } int ans=-1e18; for(int i=1;i<=n-1;i++){ b[i]=abs(a[i]-a[i+1]); } int ma=-1e18; for(int i=1;i<=n-1;i++){ int v=b[i]*(i%2?1:-1); if(ma+v>v){ ma=ma+v; }else{ ma=v; } ans=max(ans,ma); } ma=0; for(int i=2;i<=n-1;i++){ int v=b[i]*(i%2?-1:1); if(ma+v>v){ ma=ma+v; }else{ ma=v; } ans=max(ans,ma); } cout<<ans<<endl; return 0; }

    B. Weird journey

    题意:

    给定n个点m条边的无向图,可能存在自环和重边,但是保证每个点的自环最多一个

    先将定义good path为m-2条路径经过两次,剩下2条路径经过一次 问图中有多少条不同的good path 两条good path不同当前仅当经过一次的路径集不同

    数据范围:n,m<=1e6

    解法:

    题目可以转换为将图中的所有边复制一次,然后删除两条边,判断是否存在欧拉路

    去掉没有边的孤立点之后,要保证图连通,这是欧拉路存在的前提 复制之后每个点的度数翻倍,所以所有点的度数一定是偶数,现在只需要判断删边之后是否能构造出欧拉路。

    1.如果删掉的是不相邻且都不是自环的两条边,会出现4个奇数度,不存在欧拉路 2.如果删掉的是相邻且都不是自环的两条边,会出现2个奇数度,存在欧拉路 3.如果删掉的是两个自环,不会出现奇数度,存在欧拉路 4.如果删掉的一个是自环,一个不是自环,会出现两个奇数度,存在欧拉路

    1的情况不好计算,考虑计算2,3,4的情况累加 3,4可以合并成自环可以和任意边配对

    遇到自环(x,x)则ans+=m-1 否则(x,v)则ans+=(d[x]-1)+(d[v]-1)+(d[x]和d[v]之外的自环数) 发现(d[x]和d[v]之外的自环数)不好统计 改变一下写法,统计的度数的时候自环不增加度数,设自环数量为cir 则(x,v)时变为ans+=(d[x]-1)+(d[v]-1)+cir

    因为存在重复计算,最后需要除以2

    code:

    #include<bits/stdc++.h> using namespace std; #define int long long const int maxm=1e6+5; int u[maxm],v[maxm]; int mark[maxm]; int d[maxm]; int pre[maxm]; int n,m; int ffind(int x){ return pre[x]==x?x:pre[x]=ffind(pre[x]); } signed main(){ ios::sync_with_stdio(0); cin>>n>>m; for(int i=1;i<=n;i++)pre[i]=i; // int cir=0; for(int i=1;i<=m;i++){ cin>>u[i]>>v[i]; mark[u[i]]=mark[v[i]]=1;//标记非孤立点 if(u[i]==v[i]){ cir++; }else{ d[u[i]]++,d[v[i]]++; pre[ffind(u[i])]=ffind(v[i]);//并查集维护连通性 } } int cnt=0; for(int i=1;i<=n;i++){ if(mark[i]&&pre[i]==i){ if(++cnt>1)break; } } if(cnt!=1){ cout<<0<<endl;return 0; } int ans=0; for(int i=1;i<=m;i++){ if(u[i]==v[i]){//自环可以和其他任意边匹配 ans+=m-1; }else{ ans+=(d[u[i]]-1)+(d[v[i]]-1)+cir;//度数统计自环,所以才可以直接加上cir } } ans/=2;//去重 cout<<ans<<endl; return 0; }

    C. The Great Mixing

    题意:

    给定n,和一个长度为k的数组a,表示有k种不同浓度的可乐,a(i)表示第i种可乐的浓度为a(i)/1000 现在要调配出浓度为n/1000的可乐,问最少需要多少杯可乐混合, 混合的时候不仅浓度相加,总体积也要相加(显然) 每种可乐有无限杯,且每次只能整杯整杯混合。 如果无解则输出-1

    数据范围:n<=1e3,k<=1e6

    解法:

    假设需要m杯,第i杯的浓度是b(i),那么(b(1)+b(2)+…b(m))/(1000m)=n/1000 将式子去分母变为b(1)+b(2)+…b(m)=m*n 再转化一下变为(b(1)-n)+(b(2)-n)+…(b(m)-n)=0 问题就变为计算最小需要多少个b(i)-n,使得他们相加等于0

    可以用bfs计算 注意题中k<=1e6,但是每种可乐无限杯且浓度范围只有1000,所以去重一下将k缩小到1e3(否则bfs的时候会炸)

    总结: 因为原式存在除法,可能会出现小数,因此应该先想是否能通过转化式子将除法转化为乘法

    code:

    #include<bits/stdc++.h> using namespace std; #define int long long const int maxm=1e6+5; int a[maxm]; int n,k; signed main(){ ios::sync_with_stdio(0); cin>>n>>k; for(int i=1;i<=k;i++){ cin>>a[i]; a[i]=a[i]-n; } //去重,将1e6转为1e3 sort(a+1,a+1+k); k=unique(a+1,a+1+k)-a-1; // map<int,int>mark; queue<int>q; for(int i=1;i<=k;i++){ mark[a[i]]=1; q.push(a[i]); } int now=0; while(!q.empty()){ now++; int len=q.size(); while(len--){ int x=q.front();q.pop(); if(x==0){ cout<<now<<endl;return 0; } for(int i=1;i<=k;i++){ int t=x+a[i]; if(t<-500||t>500)continue;//剪枝 if(!mark.count(t)){ mark[t]=1; q.push(t); } } } } cout<<-1<<endl; return 0; }
    Processed: 0.012, SQL: 9