abc_467训练记录

C

注意到一个位置要么操作,要么不操作,这是一个经典的 dp 题。

\(dp_{i,0/1}\) 表示当前考虑当第 \(i\) 个位置,且当前位置不修改/修改的最小所需花费。

可以将 \(a_i,b_i\) 都看做 bool 变量

转移方程如下:

  • \(dp_{i,0}=min(dp_{i-1,0}[(a_i+a_{i-1})\mod 2=b_{i-1}],dp_{i-1,1}[(a_i+a_{i-1}\oplus 1)\mod 2=b_{i-1}])\)
  • \(dp_{i,1}=min(1+dp_{i-1,1}[(a_i\oplus 1+a_{i-1})\mod 2=b_{i-1}],1+dp_{i-1,1}[(a_i\oplus 1+a_{i-1}\oplus 1)\mod 2=b_{i-1}])\)

答案取 \(min(dp_{n,0},dp_{n,1})\)

时间复杂度为\(O(n)\)

D

设第一个圆上的两个点为\(A,B\),第二个圆上的两个点为\(C,D\)

不难发现:圆心必定交与\(AB\)\(CD\)的垂直平分线上。

\(AB\)\(CD\)不平行,则一定存在这样的圆心,且唯一存在;

否则,若\(AB\)\(CD\)是平行的,那么如果存在这样的圆心,一定要求两条垂直平分线是重合的,满足这样的条件则要\(ABDC\)是等腰梯形。

可以用点积判断两个向量是否平行。

\(ABDE\)是等腰梯形,则要求\(\ang CAB=\ang ABD\),则\(\vec{AB}\cdot \vec{AC}=\vec{BA}\cdot\vec{BD}\)

#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<cmath>
#include<bitset>
#include<cstring>
#include<queue>
#include<iomanip>
#define mt make_tuple
//cout << setprecision(3) ; //输出3位小数,3.142
#define pb push_back
#define fi first
#define se second
using namespace std;
using ll=long long;
typedef pair<int,int>pii;
typedef tuple<int,int,int>ti3;
const int N=3e5+10;
const double eps=1e-9;
const double pi=acos(-1.0);
int sgn(double x){
    if(fabs(x)<eps)return 0;
    if(x<eps)return -1;
    return 1;
}
int dcmp(double x,double y){
    return sgn(x-y);
}
template<class T>
class Point{
public:
    T x,y;
    Point():x(0),y(0){}
    Point(T x,T y):x(x),y(y){}
    Point operator +(const Point B){
        return Point(x+B.x,y+B.y);
    }
    Point operator -(const Point B){
        return Point(x-B.x,y-B.y);
    }
    bool operator==(const Point&a){
        return x-a.x==0&&y-a.y==0;
    }
    T operator*(const Point&a) const{//dot
        return this->x*a.x+this->y*a.y;
    }
    T operator^(const Point&a){//cross
        return this->x*a.y-a.x*this->y;
    }
    T len2(){
        return (*this)*(*this);
    }
    void in(){
        cin>>x>>y;
    }
    
};
void Silverwolf(){
    Point<ll>p1,p2,p3,p4;
    p1.in();
    p2.in();
    p3.in();
    p4.in();
    if((p1==p3)&&p2==p4)cout<<"Yes\n";
    else if(p1==p4&&p2==p3)cout<<"Yes\n";
    else{
        Point<ll>v21=p2-p1;
        Point<ll>v43=p4-p3;
        Point<ll>v31=p3-p1;
        Point<ll>v42=p4-p2;
        if((v21^v43)==0){
            //平行
            //梯形
            if(v21*v31+v21*v42==0){
                cout<<"Yes\n";
            }else cout<<"No\n";
        }else cout<<"Yes\n";
        
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;cin>>T;while(T--)
    Silverwolf();
    //愿艾利欧三度为你窥见,令你的生命永不停歇,骇入永远成功,游戏永远胜利。
    //游戏就只是为了游戏,仅此而已
    //acm这种东西,三两下搞定就好啦
    return 0;
}

E

如果固定第一个位置,则对于 \([2,n]\) 的所有位置, 它的贡献都被固定了,且固定为 \(c_i\)

不妨考虑每次增加第一个位置,发现对于偶数位,它的贡献是在模意义下 -1 ,对于奇数位,它的贡献是在模意义下 +1。这时,我们得到的东西时间复杂度是\(O(nm)\)的。

不妨先只考虑偶数位,它的贡献在枚举第一个位置为 \(c_i+1\) 时刻会跃变。在\([1,c_i]\) 时刻,其贡献等价于每一次都是 -1,在 \([c_i+1,m)\) 时刻,它的贡献会在此时刻从 \(m-1\) 开始继续往下 -1 。我们可以假设对于所有位置,在 \(t\) 时刻,贡献全部是 \(-t\) 的,而在贡献跃变时刻,将贡献改成$ m-1+c_i+1$ 开始继续维护需要降低的贡献,可以用优先队列维护当前最快要跃迁的位置。

那奇数位呢?同理,它的贡献会在 \(T=m-c_i\) 时刻跃变,在\([1,T]\)时刻,每次贡献都是要 \(+1\)。在\([1,T]\)时刻,其贡献每次都会要+1,在\([T+1,m)\)时刻,它的贡献会从此最早时刻从\(-T\)开始向上涨。

这样如果枚举所有时刻,那么已经可以用优先队列快速维护贡献,每个位置至多被出队一次。

你发现,对于两个时刻\([t_1,t_2]\)之间,每次都会上涨或下降相同的贡献,因此,我的贡献最优值一定会在端点处取到,所以我改成枚举所有端点值。

综上,时间复杂度为\(O(n\log n)\)

#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<cmath>
#include<bitset>
#include<cstring>
#include<queue>
#include<iomanip>
#define mt make_tuple
//cout << setprecision(3) ; //输出3位小数,3.142
#define pb push_back
#define fi first
#define se second
using namespace std;
using ll=long long;
typedef pair<int,int>pii;
typedef tuple<int,int,int>ti3;
const int N=3e5+10;
//bi=0 or 1
const int inf=1e9+7;
void Silverwolf(){
    int n,m;
    cin>>n>>m;
    vector<int>a(n+1,0),b(n,0),c(n+1,0);
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<n;i++)cin>>b[i];
    //固定 a[1]
    //对于 [2,n] 的所有位置 计算一下需要修改的位置有多少个
    auto mo=[&](int x){
        return (x%m+m)%m;
    };
    for(int i=2;i<=n;i++){
        c[i]=mo(b[i-1]-a[i-1]-a[i]);
        a[i]=mo(b[i-1]-a[i-1]);
    }
    ll ans=0;
    for(int i=2;i<=n;i++)ans+=c[i];
    ll sum=0,cnt1=0,cnt2=0;
    vector<int>times;//枚举时间
    priority_queue<int,vector<int>,greater<int>>que;// 小根
    priority_queue<int>que2;//大根
    auto add=[&](int x)->void{
        if(x>=0&&x<m)times.pb(x);
    };
    for(int i=2;i<=n;i++){
        if(i%2==0){
            add(c[i]);
            add(c[i]+1);
            add(c[i]-1);
            que.push(c[i]);
            sum+=c[i];
            cnt1++;
        }
        else{
            add(m-c[i]);
            add(m-c[i]-1);
            add(m-c[i]+1);
            que2.push(c[i]);
            sum+=c[i];
            cnt2++;
        } 
    }
    sort(times.begin(),times.end());
    times.erase(unique(times.begin(),times.end()),times.end());
    sort(times.begin(),times.end());
    int lt=0;
    for(auto t:times){
        while(que.size()&&que.top()==t-1){
            auto tmp=que.top();
            sum-=tmp;
            que.pop();
            tmp=m-1+t;
            sum+=tmp;
            que.push(tmp);
        }
        while(que2.size()&&que2.top()+t==m){
            auto tmp=que2.top();
            que2.pop();
            sum-=tmp;
            tmp=-t;
            sum+=tmp;
            que2.push(tmp);
        }
        ans=min(ans,t+sum-t*cnt1+t*cnt2);
    }
    cout<<ans<<'\n';
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    // int T;cin>>T;while(T--)
    Silverwolf();
    //愿艾利欧三度为你窥见,令你的生命永不停歇,骇入永远成功,游戏永远胜利。
    //游戏就只是为了游戏,仅此而已
    //acm这种东西,三两下搞定就好啦
    return 0;
}

导航