杂题

计算几何

1

一个多边形与一个圆求交
三角剖分
多边形每一个顶点向圆心连线,计算每一个三角形与圆面积交。
问题转化为三角形。
分五种情况讨论

2

铁人三项
设两段占比例分别为x,y, 列一堆方程。

3

询问多边形内可以放置的最大的圆。
二分,每条边往里缩,半平面。

4

三角形面积并
扫描线 每一段是一些梯形和三角形,利用梯形面积公式
simpson积分 圆的面积并
(4f(mid)+f(L)+f(R))/6 ( 4 ∗ f ( m i d ) + f ( L ) + f ( R ) ) / 6

5

抛物线 切一刀 求面积
一个结论:面积是与切点三角形面积的三分之四
Alt text

6

扫描线
平面上一些不相交的图形构成一个树形结构
把每个圆拆成上下两个点 扫描线 扔进set里

7

三维差积
五个点的三维凸包

4514

树形dp

1

n n 个点的树,边有边权,把其中k 个点涂成黑色,其余的点涂成白色,最大化:每两个黑点间距离和+每两个白点间的距离和。

直接设 f[i][j] f [ i ] [ j ] 表示 i i 子树里选 j 个黑点的最大值。然后发现不能转移…

考虑到每条边是独立的,因此这样设状态:令 f[i][j] f [ i ] [ j ] 表示 i i 的子树里选 j 个黑点最多对答案有多少贡献。

一条边对答案的贡献就是 权值 × × (左边黑点数 × × 右边黑点数 + + 左边白点数 × 右边白点数)。

void dfs(int u,int fa)
{
    size[u]=1;
    memset(f[u],-1,sizeof(f[u]));
    f[u][0]=f[u][1]=0;
    for(int i=head[u];i;i=ed[i].next)
    {
        int v=ed[i].to;
        if(v==fa) continue;
        dfs(v,u);
        size[u]+=size[v];
    }
    for(int i=head[u];i;i=ed[i].next)
    {
        int v=ed[i].to;
        if(v==fa) continue;
        for(int j=min(size[u],k);j>=0;j--)
        {
            for(int l=0,lim=min(size[v],j);l<=lim;l++)
            {
                if(~f[u][j-l])
                {
                    ll val=1ll*ed[i].w*(1ll*l*(k-l)+1ll*(size[v]-l)*(n-size[v]-k+l));
                    f[u][j]=max(f[u][j],f[u][j-l]+f[v][l]+val);
                }
            }
        }
    }
}

2

山贼集团
题意: n n 个点的树,根为 1 ,设置 p p 个黑点(有编号) (p<=12) ,每个黑点可以管理它到根的路径上的每一个点,某些黑点同时管理同一个点会获得一定价值/损失,在不同的点设置不同的黑点要花费不同的代价,要求最大化收益。
p<=12 p <= 12 状压。 f[i][j] f [ i ] [ j ] 表示 i i 为根的子树内选择 j 的集合的最大收益。转移的时候先不考虑 i i ,令 g[j] 表示 i i 的子树中除去 i 的点选择 j j 的集合的最大价值(类似一个背包)。然后考虑 i 这个点放那些黑点,转移一下。

3

题意: n n 个点的树,设置 k 个监察点,每个监察点可以监视与它相邻的点,但不能监视自己。要监视所有点,求方案数。

f[i][j][1/0][1/0] f [ i ] [ j ] [ 1 / 0 ] [ 1 / 0 ] 表示 i i 子树里设置 k 个监察点,根有/没有设置监察点,有/没有被监视。然后分情况转移。

圆方树

神奇的建树方法。
对每个点双新建一个方点,把这个点双里原来的边去掉,并把每个点连向这个方点。这样原图就是一棵树,并且原图的割点就是圆方树中度数大于 1 1 的圆点。
如果把一个点双里深度最小的点称作这个点双的“根”,那么一般来讲为了不重复维护信息,每个方点维护所在点双里除了根的信息,然后对于dfs树的根单独处理。
代码在 tarjan 的基础上稍作修改:

void tarjan(int u,int father)
{
    vis[u]=1,dfn[u]=low[u]=++tim,sta[++top]=u;
    for(int i=head1[u];i;i=ed1[i].next)
    {
        int v=ed1[i].to;
        if(v==father) continue;
        if(!dfn[v]) 
        {
            tarjan(v,u);
            if(low[v]>=dfn[u])
            {
                point++;
                add2(point,u),add2(u,point);
                int now=-1;
                do{
                    now=sta[top--];
                    bel[now]=point;
                    add2(now,point),add2(point,now);
                    sccsize[point-n]++;
                }while(now!=v);
            }
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    }
}

1

无向联通图,点有点权,每次询问两点间简单路径的并的第 k k <script type="math/tex" id="MathJax-Element-35">k</script> 大点权,待修改。

首先,简单路径是不重复经过点的路径。简单路径并的点也就是圆方树上两个点所属方点间路径上的点所在的点双的点的集合…

然后就是圆方树上树状数组套主席树啦。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct edge{
    int to,next;
}ed1[400010],ed2[400010];
struct node{
    int x,y,k,opt;
}q[200010];
int lowbit(int x){
    return x&(-x);
}
int ls[20000010],rs[20000010],sum[20000010],root[200010],a[200010],b[200010],len[200010],f[200010][19],head1[200010],head2[200010],size1,size2,tim,bel[200010];
int sccsize[200010],tmp[2][200010],cnt[2],point,dfn[200010],low[200010],n,m,T,num,vis[200010],sta[200010],top,A[200010],B[200010],tot,deep[200010];

inline void add1(int from,int to)
{
    ed1[++size1].to=to;
    ed1[size1].next=head1[from];
    head1[from]=size1;
}
inline void add2(int from,int to)
{
    ed2[++size2].to=to;
    ed2[size2].next=head2[from];
    head2[from]=size2;
}
inline void input()
{
    scanf("%d%d%d",&n,&m,&T),num=n;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add1(u,v),add1(v,u);
    }
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    for(int i=1;i<=T;i++) 
    {
        scanf("%d%d%d",&q[i].opt,&q[i].x,&q[i].y);
        if(q[i].opt==2) scanf("%d",&q[i].k);
        else b[++num]=q[i].y;
    }
    sort(b+1,b+num+1);
    num=unique(b+1,b+num+1)-b-1;
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+num+1,a[i])-b;
}
void tarjan(int u,int father)
{
    vis[u]=1,dfn[u]=low[u]=++tim,sta[++top]=u;
    for(int i=head1[u];i;i=ed1[i].next)
    {
        int v=ed1[i].to;
        if(v==father) continue;
        if(!dfn[v]) 
        {
            tarjan(v,u);
            if(low[v]>=dfn[u])
            {
                point++;
                add2(point,u),add2(u,point);
                int now=-1;
                do{
                    now=sta[top--];
                    bel[now]=point;
                    add2(now,point),add2(point,now);
                    sccsize[point-n]++;
                }while(now!=v);
            }
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    }
}
void dfs(int u)
{
    A[u]=++tot;
    if(u>n) len[u]+=sccsize[u-n];
    for(int i=head2[u];i;i=ed2[i].next)
    {
        int v=ed2[i].to;
        if(v==f[u][0]) continue;
        f[v][0]=u;
        deep[v]=deep[u]+1;
        len[v]+=len[u];
        dfs(v);
    }
    B[u]=tot;
}
void update(int &root,int l,int r,int x,int k)
{
    if(!root) root=++tot;
    sum[root]+=k;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) update(ls[root],l,mid,x,k);
    else update(rs[root],mid+1,r,x,k);
}
inline void modify(int pos,int x,int k)
{
    for(int i=pos;i<=point;i+=lowbit(i)) update(root[i],1,num,x,k);
}
inline void make_tree()
{
    point=n;
    tarjan(1,0);
    deep[1]=1;
    dfs(1);tot=0;
    for(int j=1;j<=18;j++)
        for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
    for(int i=2;i<=n;i++)
    {
        modify(A[bel[i]],a[i],1);
        modify(B[bel[i]]+1,a[i],-1);
    }
}
inline int Lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    for(int j=18;j>=0;j--)
        if(deep[f[x][j]]>=deep[y]) x=f[x][j];
    if(x==y) return x;
    for(int j=18;j>=0;j--)
        if(f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
    return f[x][0];
}
int query(int l,int r,int k,int w)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int zz=0;
    for(int i=1;i<=cnt[0];i++) zz+=sum[ls[tmp[0][i]]];
    for(int i=1;i<=cnt[1];i++) zz-=sum[ls[tmp[1][i]]];
    if(w>=l&&w<=mid) zz++;
    if(k<=zz)
    {
        for(int i=1;i<=cnt[0];i++) tmp[0][i]=ls[tmp[0][i]];
        for(int i=1;i<=cnt[1];i++) tmp[1][i]=ls[tmp[1][i]];
        return query(l,mid,k,w);
    }
    for(int i=1;i<=cnt[0];i++) tmp[0][i]=rs[tmp[0][i]];
    for(int i=1;i<=cnt[1];i++) tmp[1][i]=rs[tmp[1][i]];
    return query(mid+1,r,k-zz,w);
}
inline void solve()
{
    for(int i=1;i<=T;i++)
    {
        int opt=q[i].opt,x=q[i].x,y=q[i].y,k=q[i].k;
        if(opt==1)
        {
            if(x==1)
            {
                a[x]=lower_bound(b+1,b+num+1,y)-b;
                continue;
            }
            modify(A[bel[x]],a[x],-1),modify(B[bel[x]]+1,a[x],1);
            a[x]=lower_bound(b+1,b+num+1,y)-b;
            modify(A[bel[x]],a[x],1),modify(B[bel[x]]+1,a[x],-1);
        }
        else
        {
            int lca=Lca(x,y),ff=f[lca][0];
            int zz=len[x]+len[y]-len[lca]*2+1;
            if(lca>n) zz+=sccsize[lca-n];
            if(k>zz)
            {
                puts("-1");
                continue;
            }
            if(A[x]>A[y]) swap(x,y);
            if(lca>n) zz=a[ff];
            else zz=a[lca];
            cnt[0]=cnt[1]=0;
            for(int i=A[x];i;i-=lowbit(i)) tmp[0][++cnt[0]]=root[i];
            for(int i=A[y];i;i-=lowbit(i)) tmp[0][++cnt[0]]=root[i];
            for(int i=A[lca];i;i-=lowbit(i)) tmp[1][++cnt[1]]=root[i];
            for(int i=A[ff];i;i-=lowbit(i)) tmp[1][++cnt[1]]=root[i];
            printf("%d\n",b[query(1,num,k,zz)]);
        }
    }
}
int main()
{
    input();
    make_tree();
    solve();
    return 0;
}

2

铁人两项
思路是在圆方树上枚举所有圆点作为中间点,然后用全部方案减去不合法的。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值