ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ABC 263

2022-08-08 01:04:19  阅读:184  来源: 互联网

标签:ABC return int double 交点 263 res dp


E.Sugoroku 3(概率 DP)

Problem

一个数轴上标有\(1\)到\(N\),第\(i\)个点有一个骰子,骰子上的数字标号从\(0\)到\(A_i\),在第\(i\)个点上可以投掷骰子,投掷出来的数字代表下一步可以前进多少步,每个数字被投掷出来的概率相同,问从\(1\)号点到\(N\)号点期望投掷骰子多少次,答案对\(998244353\)取模

  • \(2\le N\le 2\times 10^5\)
  • \(1\le A_i\le N-i\)
  • 全部都是正整数

Solve

令\(dp[i]\)表示从\(i\)到\(N\)号点需要投掷骰子的期望次数

概率\(DP\)一般有两种思考方式

  • 从终点出发更新前面的状态
  • 从起点出发更新后面的状态

这里\(DP\)的定义方法实际上是为了从终点出发更新前面的状态,那么显然有\(dp[N]=0\)

考虑\(i=N-1\text{,}N-2\text{,}\cdots,1\),则有

\[dp[i]=\frac{dp[i]}{A_i+1}+\frac{\sum_{j=1}^{A_i}dp[i+j]}{A_i+1}+1\\ dp[i](1-\frac{1}{A_i+1})=\frac{\sum_{j=1}^{A_i}dp[i+j]}{A_i+1}+1\\ dp[i]=\frac{\sum_{j=1}^{A_i}dp[i+j]}{A_i}+\frac{A_i+1}{A_i} \]

注意到\(\sum_{j=1}^{A_i}dp[i+j]\)是一段连续的和,所以可以用前缀和优化

\[dp[i]=\frac{suf[i+1]-suf[i+A_i+1]}{A_i}+\frac{A_i+1}{A_i} \]

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=998244353;
ll power(ll x,int y){
    ll res=1;
    while(y){
        if(y&1)res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin>>n;
    vector<int>A(n+1);
    for(int i=1;i<n;i++) cin>>A[i];
    vector<ll>dp(n+1),suf(n+2);
    dp[n]=0;
    for(int i=n-1;i>=1;i--){
        ll inv=power(A[i],mod-2);
        dp[i]=((suf[i+1]-suf[i+A[i]+1]+mod)%mod*inv%mod+(A[i]+1)*inv%mod)%mod;
        suf[i]=(suf[i+1]+dp[i])%mod;
    }
    cout<<dp[1]<<'\n';
    return 0;
}

F - Tournament

Problem

有\(2^N\)个人,从\(1\)到\(2^N\)标号,进行剪刀石头布的比赛。令\(2M\)表示人的总数,因此有\(M\)对选手,第\(i\)对是标号为\((2i-1)\)和\((2i)\)的人比赛,胜利的人会进行下一场,胜利人的保持彼此之间的编号的相对大小不变继续比赛,知道决出一个人胜利,因此总共有\(N\)次比赛。如果第\(i\)个人获胜\(j\)场会得到\(C_{ij}\)元,请求出\(2^N\)个人可能获得的总钱数的最大值

Solve

这个比赛过程实际上是一棵满二叉树,一开始的状态是从最底下的叶子节点开始,树高为\(N\)

所以考虑树形 DP,令\(dp[i][j]\)表示在第\(i\)号节点获胜的人最终可以获胜\(j\)场的最大收益

不妨假设第\(i\)号节点的左儿子获胜,并且假设到\(i\)号节点进行了\(cnt\)场比赛,那么转移就是\(dp[i][j]=dp[ls][i]+dp[rs][cnt-1]\),由于是左儿子获胜,所以\(i\)号节点要继承左儿子的,而右儿子在这一轮比赛中输掉了,所以在右儿子获胜的人最终只能获胜\(cnt-1\)场就止步于此了,所以用这两个的和来转移即可。根据对称性,右儿子的获胜的转移也是一样的。

Code

#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin>>n;
    vector<vector<int>> c((1<<n)+1,vector<int>(n+1));
    for(int i=1;i<=(1<<n);i++){
        for(int j=1;j<=n;j++){
            cin>>c[i][j];
        }
    }
    vector<vector<ll>>dp((1<<(n+1))+1,vector<ll>(n+1)); //2^{n+1}个结点
    auto dfs=[&](auto self,int rt,int l,int r,int cnt)->void{ //i,j分别表示左右两边获胜者获胜的场数
         if(l==r){
            for(int i=1;i<=n;i++) dp[rt][i]=c[l][i];
                return ;
         }
         int mid=(l+r)>>1;
         self(self,ls,l,mid,cnt-1);
         self(self,rs,mid+1,r,cnt-1);
         for(int i=0;i<=n;i++)
            dp[rt][i]=max(dp[ls][i]+dp[rs][cnt-1],dp[ls][cnt-1]+dp[rs][i]);
    };
    dfs(dfs,1,1,1<<n,n);
    cout<<dp[1][n]<<'\n';
}

Ex - Intersection 2(计算集合、二分、极角排序、树状数组)

Problem

给定\(N\)条线段,这\(N\)条线段总共有\(\frac{N(N-1)}{2}\)个交点,并且保证没有两条直线平行。输出距离原点距离第\(K\)大的点的距离。

线段以\(A_ix+B_iy+C_i=0\)的形式给出

  • \(2\le N\le 5\times 10^4\),
  • \(-1000\le |A_i|,|B_i|,|C_i|\le 1000\)
  • \(A_i\ne 0\)或\(B_i\ne 0\)

Solve

考虑二分这个距离的大小,为什么可以二分?若以原点为圆心,二分的距离为半径,显然,半径越大,圆内包含的交点数越多,半径越小,园内包含的交点数越少,具有单调性,所以可以用二分。

接着考虑怎么求圆内的交点个数,我们可以把所有与圆有两个交点的直线的交点先算出来,然后把它们按照极角序排序,选定一个方向,显然,一条直线和其他直线在圆内有交点,当且仅当它的两个交点和其他直线的两个交点是交错的,也就是我们可以统计一条直线会贡献多少个交点,假设这条直线开始与它的某一个交点\(l\),我们在扫到它的另一个端点\(r\)的时候把它的交点贡献加入答案,然后删除它的贡献。
image

然后直线与圆的交点就用解析法求就好了

Code

#include <bits/stdc++.h>
using namespace std;
const double eps=1e-8;
int sgn(double x){
    if(fabs(x)<eps)return 0;
    else if(x>0) return 1;
    else return -1;
}
int dcmp(double x,double y){
    if(fabs(x-y)<eps) return 0;
    else if(x>y)return 1;
    else return -1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double x_,double y_):x(x_),y(y_){}
    double operator ^ (const Point &t)const{
        return x*t.y-y*t.x;
    }

};

bool comp(Point l, Point r) { //极角排序
    bool lf = l.y > 0;
    bool rf = r.y > 0;
    if (lf != rf) return !lf;
    if (lf)
        return l.x > r.x;
    else
        return l.x < r.x;

    //浮点数极角排序用atan2,但复杂度太高了
    // if(sgn(atan2(l.y, l.x) - atan2(r.y, r.x)) == 0) //dcmp为判断浮点数是否为0的函数
    //     return l.x < r.x;
    // return atan2(l.y, l.x) < atan2(r.y, r.x);
}
struct Circle{
    Point c;
    double r;
    Circle(){}
    Circle(Point c_,double r_):c(c_),r(r_){}

};
struct Line{
    double A,B,C;
    Line(){}
    Line(double A_,double B_,double C_):A(A_),B(B_),C(C_){}
};
vector<Point> crosspoint(Circle cir,Line l){
    //用解析法求交点
    vector<Point> res;
    double d=abs(l.C)/sqrt(l.A*l.A+l.B*l.B);
    if(dcmp(d,cir.r)>=0) return res;
    if(l.A==0){
        double y=-l.C/l.B;
        double x=sqrt(cir.r*cir.r-y*y);
        res.emplace_back(x,y);
        res.emplace_back(-x,y);
        return res;
    }
    if(l.B==0){
        double x=-l.C/l.A;
        double y=sqrt(cir.r*cir.r-x*x);
        res.emplace_back(x,y);
        res.emplace_back(x,-y);
        return res;
    }
    double a=l.B*l.B+l.A*l.A,
           b=2*l.A*l.C,
           c=l.C*l.C-l.B*l.B*cir.r*cir.r;

    double x1=(-b+sqrt(b*b-4*a*c))/(2*a);
    double x2=(-b-sqrt(b*b-4*a*c))/(2*a);

    double y1=(-l.C-l.A*x1)/l.B;
    double y2=(-l.C-l.A*x2)/l.B;

    res.emplace_back(x1,y1);
    res.emplace_back(x2,y2);
    return res;
}

const int N=1e5+10;
int tr[N];

void add(int p,int x){
    p++;
    for(;p<=100000;p+=p&-p) tr[p-1]+=x;
}
int ask(int p){
    int res=0;
    for(;p;p-=p&-p) res+=tr[p-1];
        return res;
}
int sum(int l,int r){
    return ask(r)-ask(l);
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout<<fixed<<setprecision(10);
    int n,k;
    cin>>n>>k;
    vector<Line>line(n+1);
    for(int i=1;i<=n;i++){
        double a,b,c;
        cin>>a>>b>>c;
        line[i]=Line(a,b,c);
    }
    auto check=[&](Circle c)->int{

        vector<pair<Point,int>>v;
        for(int i=1;i<=n;i++){
           auto p=crosspoint(c,line[i]);
           if(p.size()<2) continue;
           v.emplace_back(p[0],i);
           v.emplace_back(p[1],i);
        }

        sort(v.begin(), v.end(),[&](pair<Point,int> a,pair<Point,int> b){
            return comp(a.first,b.first);
        });

        vector<int>l(n+1,-1);
        int cnt=0;
        for(int i=0;i<int(v.size());i++){
           int id=v[i].second;
           if(l[id]==-1){
              add(i,1);
           }else{
              add(l[id],-1);
              cnt+=sum(l[id],i);
           }
           l[id]=i;
        }

        return cnt;
    };
    double l=0,r=1e9;
    Point o=Point(0,0);
    for(int i=1;i<=50;i++){
        double mid=(l+r)/2;
        Circle c=Circle(o,mid);
        if(check(c)>=k) r=mid;
        else l=mid;
    }
    cout<<l<<'\n';
}

标签:ABC,return,int,double,交点,263,res,dp
来源: https://www.cnblogs.com/Arashimu0x7f/p/16560352.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有