6.27联考题解

A:
给定单位圆上n个点,求在其中挑选k个点,要求它们构成的凸包包含圆心,求凸包的最大面积

考虑枚举凸包上弧度最小的点,做个dp, f [ i ] [ j ] 表示dp到第 j 个点,已经选了 k 个点且第 j 个点是第 k 个点的凸包最大面积
转移就枚举上一个选的点,因为点是以弧度形式给出的,可以直接用弧度计算面积
f [ i ] [ j ] = f [ i 1 ] [ k ] + s i n ( r a d j r a d k )
推公式(反正我还不会其实是没看猜结论可知转移有决策单调性,维护单调队列,可以把转移做到均摊 O ( l o g )
那么我们现在的复杂度是 O ( n 3 l o g n )

题解说随机n/k个起始点dp可以做到很高的正确率(然而我写出来的并不是这样的不知道是不是姿势问题)

不难发现第一维那个已经选了多少个点可以用wqs二分弄掉
复杂度变成 O ( n 2 l o g 2 n )

然后不知怎么的(可能是数据水…)我发现只要枚举前10个点做为起始点dp后面的不管他就过了….

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define ld long double
#define fir first
#define sec second
#define inf 1e7
using namespace std;

const int maxn = 2100;
const ld pi  = acos(-1);

int n,K;
pair<ld,int> ang[maxn];
struct point
{
    int i;
    ld x,y,ang;
    friend inline point operator -(const point &x,const point &y){return (point){x.i,x.x-y.x,x.y-y.y};}
    friend inline ld operator *(const point &x,const point &y){return x.x*y.y-x.y*y.x;}
}zero,p[maxn];
int R[maxn];
ld _sin[maxn][maxn];

struct data
{
    ld f; int pf,k;
}f[maxn][maxn];
struct node{int i,ti;}q[maxn]; int head,tail;
int ans[maxn],ansn; ld ansnow;

namespace Subtask1
{
    const ld eps = 1e-9;
    int ST[maxn],stn;
    void dp()
    {
        ansnow=-inf; ansn=0;
        for(int i=1;i<=10&&i+K-1<=n;i++)
        {
            f[2][i].pf=0;
            for(int j=i+1;j<=R[i];j++) f[2][j]=(data){_sin[i][j],i};
            for(int j=R[i]+1;j<=n;j++) f[2][j].f=-1;

            for(int k=3;k<=K;k++)
            {
                head=1,tail=0;
                for(int j=i+1;j<=n;j++)
                {
                    while(head<=tail&&R[q[head].i]<j) head++;
                    while(head+1<=tail&&q[head+1].ti<=j) head++;

                    if(head<=tail)
                    {
                        int l=q[head].i;
                        f[k][j].f=f[k-1][l].f+_sin[l][j],f[k][j].pf=l;
                    }
                    else f[k][j].f=-1;

                    if(f[k-1][j].f>-1+eps)
                    {
                        while(head<=tail&&R[q[head].i]<=j) head++;

                        if(head>tail) q[++tail]=(node){j,0};
                        else
                        {
                            while(head<=tail)
                            {
                                int las=q[tail].i;
                                int l=j+1,r=R[las];
                                while(l<=r)
                                {
                                    int mid=(l+r)>>1;
                                    if(f[k-1][las].f+_sin[las][mid]<f[k-1][j].f+_sin[j][mid]) r=mid-1;
                                    else l=mid+1;
                                }
                                int tmp=r+1;
                                if(tmp<=q[tail].ti) tail--;
                                else { q[++tail]=(node){j,tmp};break; }
                            }
                        }
                    }
                }
            }
            for(int j=i+2;j<=n;j++) if(f[K][j].f>-1+eps&&(zero-p[i])*p[j]>eps&&ansnow<f[K][j].f+_sin[i][j])
            {
                ansnow=f[K][j].f+_sin[i][j];
                ansn=0; int tx=j,tk=K;
                while(tx)
                {
                    ans[++ansn]=p[tx].i;
                    tx=f[tk--][tx].pf;
                }
            }
        }
    }
}/*
namespace Subtask2
{
    #define inf 1e7
    const ld eps = 1e-12;
    int rei,rej;
    void dp(ld val)
    {
        rei=0; ld sum=-inf;
        for(int i=1;i+K-1<=n;i++)
        {
            f[i][i].pf=0;
            for(int j=i+1;j<=R[i];j++) f[i][j]=(data){_sin[i][j],i,2};
            for(int j=R[i]+1;j<=n;j++) f[i][j].f=-inf;

            head=1,tail=0;
            for(int j=i+1;j<=n;j++)
            {
                while(head<=tail&&R[q[head].i]<j) head++;
                while(head+1<=tail&&q[head+1].ti<=j) head++;

                if(head<=tail)
                {
                    int l=q[head].i;
                    ld tmp=f[i][l].f+_sin[l][j]+val;
                    if(tmp>f[i][j].f)
                    {
                        f[i][j].f=tmp;
                        f[i][j].pf=l,f[i][j].k=f[i][l].k+1;
                    }
                }

                if(f[i][j].f>-inf+eps)
                {
                    while(head<=tail&&R[q[head].i]<=j) head++;

                    if(head>tail) q[++tail]=(node){j,0};
                    else
                    {
                        while(head<=tail)
                        {
                            int las=q[tail].i;
                            int l=j+1,r=R[las];
                            while(l<=r)
                            {
                                int mid=(l+r)>>1;
                                if(f[i][las].f+_sin[las][mid]<f[i][j].f+_sin[j][mid]) r=mid-1;
                                else l=mid+1;
                            }
                            int tmp=r+1;
                            if(tmp<=q[tail].ti) tail--;
                            else { q[++tail]=(node){j,tmp};break; }
                        }
                    }
                }
            }
            for(int j=i+2;j<=n;j++) 
                if(f[i][j].f>-inf+eps&&(zero-p[i])*p[j]>eps&&sum<f[i][j].f+_sin[i][j])
                {
                    sum=f[i][j].f+_sin[i][j];
                    rei=i,rej=j;
                }
        }
    }
    void solve()
    {
        ansn=0;
        ld l=-4.0,r=0;
        while(r-l>eps)
        {
            ld mid=(l+r)/2.0;
            dp(mid);
            if(rei&&f[rei][rej].k<=K)
            {
                if(f[rei][rej].k==K)
                {
                    int tx=rej; ansn=0;
                    while(tx)
                    {
                        ans[++ansn]=p[tx].i;
                        tx=f[rei][tx].pf;
                    }
                }
                l=mid;
            }
            else r=mid;
        }
    }
}
*/
int main()
{
    freopen("light.in","r",stdin);
    freopen("light.out","w",stdout);

    srand(100000007);

    zero=(point){0,0};

    scanf("%d%d",&n,&K);
    if(K<=2)
    {
        puts("-1");
        return 0;
    }
    for(int i=1;i<=n;i++) scanf("%Lf",&ang[i].fir),ang[i].sec=i;

    sort(ang+1,ang+n+1);
    for(int i=1;i<=n;i++) p[i]=(point){ang[i].sec,cos(ang[i].fir),sin(ang[i].fir),ang[i].fir};
    for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++)
        _sin[i][j]=fabs(sin(p[j].ang-p[i].ang));
    for(int i=1;i<=n;i++)
    {
        R[i]=n;
        for(int j=i+1;j<=n;j++) if((zero-p[i])*p[j]>-1e-9) { R[i]=j-1;break; }
    }

    /*if(K<=50||K>=1500)*/ Subtask1::dp();
    //else Subtask2::solve();

    if(ansn==0) puts("-1");
    else
    {
        for(int i=ansn;i>=1;i--) printf("%d%c",ans[i],i==1?'\n':' ');
    }

    return 0;
}

B:
给一个边有权的无向图,求 i = 1 ~ n ,点 i 度数强制为1的MST上最大边的权

一个图中若一个点不是割点一定存在某种方案使得保留图中的一些边后剩下的图是一棵树且这个点的度数是1,否则一定不行

先把MST建出来,然后从小到大枚举边权,将非树边加进去,维护这次加边后哪些点从割点变成了非割点
怎么维护很难描述…
大概就是一个点一开始有个值 v a l =度数-1,代表被这个点控制的子树数量(被控制意思就是若这个点删除这个子树会和外界隔绝),一个点 v a l = 0 最早的时刻就是他最早成为非割点的时刻
然后对于加入非树边 ( x , y ) ,设他们的LCA是 t o p ,将 x , y t o p 路径上的除了 t o p 的点分别用并查集合并起来,对于一次合并 ( f a [ x ] , x ) 意味着 x 脱离 f a [ x ] 的控制,将 v a l [ f a [ x ] ] = 1 x , y 分别合并到 d e p [ t o p ] + 1 层后,若他们属于不同的并查集,就将他们合并起来, v a l [ t o p ] = 1 ,因为这两棵子树从不连通变得联通了相当于合并了起来, t o p 原本控制这两棵子树就变成了控制这一棵

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 1100000;

int n,m;
int MX[maxn],MN[maxn],ans[maxn];
struct edge{int y,c,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=(edge){y,c,fir[x]};fir[x]=len;}

struct node
{
    int u,v,w;
    friend inline bool operator <(const node x,const node y){return x.w<y.w;}
}e[maxn];
int t[maxn],tp;

int fa[maxn];
int findfa(const int x){ return fa[x]==x?x:fa[x]=findfa(fa[x]); }

multiset<int>S;
multiset<int>::iterator it;
int dfn[maxn],dfi,siz[maxn],son[maxn],par[maxn],dep[maxn],top[maxn],d[maxn];
void dfs(const int x)
{
    MN[x]=inf;
    for(int k=fir[x];k;k=a[k].nex)
    {
        MN[x]=min(MN[x],a[k].c);
        it=S.find(a[k].c); S.erase(it);
    }
    MX[x]=S.empty()?0:(*S.rbegin());
    for(int k=fir[x];k;k=a[k].nex) S.insert(a[k].c);

    dfn[x]=++dfi;
    siz[x]=1;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=par[x])
    {
        d[x]++;
        par[y]=x,dep[y]=dep[x]+1,dfs(y);
        if(siz[son[x]]<siz[y]) son[x]=y;
        siz[x]+=siz[y];
    }
}
void build(const int x,const int Top)
{
    top[x]=Top;
    if(son[x]) build(son[x],Top);
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=par[x]&&y!=son[x])
        build(y,y);
}
int LCA(int x,int y)
{
    int f1=top[x],f2=top[y];
    while(f1!=f2)
    {
        if(dep[f1]<dep[f2]) swap(f1,f2),swap(x,y);
        x=par[f1],f1=top[x];
    }
    return dep[x]<dep[y]?x:y;
}

int main()
{
    freopen("vain.in","r",stdin);
    freopen("vain.out","w",stdout);

    read(n); read(m);
    for(int i=1;i<=m;i++) read(e[i].u),read(e[i].v),read(e[i].w);
    sort(e+1,e+m+1);

    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x=e[i].u,y=e[i].v,c=e[i].w;
        int f1=findfa(x),f2=findfa(y);
        if(f1==f2) t[++tp]=i;
        else
        {
            fa[f1]=f2;
            ins(x,y,c); ins(y,x,c);
            S.insert(c);
        }
    }
    if(tp+n-1!=m)
    {
        printf("%d\n",-n);
        return 0;
    }
    dep[1]=1,dfs(1),d[1]--;
    build(1,1);
    for(int i=1;i<=n;i++) ans[i]=max(MX[i],MN[i]);

    for(int i=1;i<=n;i++) fa[i]=i;
    for(int ii=1;ii<=tp;ii++)
    {
        int i=t[ii];
        int x=e[i].u,y=e[i].v;
        if(findfa(x)==findfa(y)) continue;
        if(dfn[x]>dfn[y]) swap(x,y);

        if(dfn[x]+siz[x]>dfn[y])
        {
            y=findfa(y);
            while(dep[y]>dep[x]+1)
            {
                int ff=par[y];
                d[ff]--;
                if(!d[ff]) ans[ff]=max(ans[ff],e[i].w);
                ff=findfa(ff);
                fa[y]=ff,y=ff;
            }
        }
        else
        {
            int ff=LCA(x,y);
            x=findfa(x);
            while(dep[x]>dep[ff]+1)
            {
                int fx=par[x];
                d[fx]--;
                if(!d[fx]) ans[fx]=max(ans[fx],e[i].w);
                fx=findfa(fx);
                fa[x]=fx,x=fx;
            }
            y=findfa(y);
            while(dep[y]>dep[ff]+1)
            {
                int fy=par[y];
                d[fy]--;
                if(!d[fy]) ans[fy]=max(ans[fy],e[i].w);
                fy=findfa(fy);
                fa[y]=fy,y=fy;
            }
            if(x==y) continue;
            if(dep[x]>dep[ff]||dep[y]>dep[ff])
            {
                d[ff]--;
                if(!d[ff]) ans[ff]=max(ans[ff],e[i].w);
            }
            if(dep[x]<dep[y]) swap(x,y);
            fa[x]=y;
        }
    }

    ll re=0;
    for(int i=1;i<=n;i++) re+=d[i]?-1:ans[i];
    printf("%lld\n",re);

    return 0;
}

C:
维护一棵有根树,资瓷往一条边中间塞一个点,往一个点下放一个点,询问k个点到根路径并上的点权和

我本来以为要维护LCT,然后splay维护dfs序,对于询问建出虚树来在上面搞
然后被别人告知可以直接用LCT做,只要Access统计和的时候对这条链打个标记代表这条链在这次询问已经被统计过,下次再遇到不统计就好了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void up(int &a,const int &b){if(a<b)a=b;}
const int maxn = 410000;

int n,m;
int w[maxn],par[maxn];
struct Link_Cut_Tree
{
    int son[maxn][2],fa[maxn],fl[maxn];
    ll sum[maxn];

    bool isrt(int x){ return son[fa[x]][0]!=x&&son[fa[x]][1]!=x; }
    void pushdown(int x)
    {
        int fc=fl[x];
        up(fl[son[x][0]],fc);
        up(fl[son[x][1]],fc);
    }
    int t[maxn],tp;
    void Down(int x)
    {
        int y=x; while(!isrt(y)) t[++tp]=y,y=fa[y];
        t[++tp]=y;
        while(tp) pushdown(t[tp--]);
    }
    void pushup(int x) { sum[x]=sum[son[x][0]]+sum[son[x][1]]+w[x]; }

    void rot(int x)
    {
        int y=fa[x],z=fa[y];
        if(!isrt(y)) son[z][son[z][1]==y]=x;
        fa[x]=z;
        int l=son[y][1]==x;
        fa[son[y][l]=son[x][!l]]=y;
        fa[son[x][!l]=y]=x;
        pushup(y);
    }
    void splay(int x)
    {
        Down(x);
        for(;!isrt(x);rot(x))
        {
            int y=fa[x],z=fa[y];
            if(!isrt(y)) rot(((son[y][1]==x)^(son[z][1]==y))?x:y);
        }
        pushup(x);
    }
    int nowi;
    ll Access(int x)
    {
        ll re=0;
        for(int y=0;x;y=x,x=fa[x])
        {
            splay(x); son[x][1]=y,pushup(x);
            if(fl[x]!=nowi) 
            {
                re+=sum[x];
                if(fl[son[x][1]]==nowi) re-=sum[son[x][1]];
            }
            fl[x]=nowi;
        }
        return re;
    }
    void Link(int x,int y)
    {
        fa[y]=x;
    }
    void Cut(int x,int y)
    {
        Access(x);
        splay(y); fa[y]=0;
    }
}LCT;
ll ans;
int t[maxn],tp;

int main()
{
    freopen("death.in","r",stdin);
    freopen("death.out","w",stdout);

    read(n),read(m);
    for(int i=1;i<=n;i++) read(w[i]);
    for(int i=2;i<=n;i++)
    {
        int x; read(x);
        LCT.Link(par[i]=x,i);
    }

    while(m--)
    {
        int type; read(type);
        if(type==1)
        {
            int x,c; read(x),read(c); x=(x+ans)%n+1;
            w[++n]=c;
            LCT.Link(par[n]=x,n);
        }
        else if(type==2)
        {
            int x,c; read(x),read(c); x=(x+ans)%n+1;
            w[++n]=c;
            LCT.Cut(par[x],x);
            par[n]=par[x],par[x]=n;
            LCT.Link(par[n],n);
            LCT.Link(par[x],x);
        }
        else
        {
            LCT.nowi++;
            read(tp); for(int i=1;i<=tp;i++) read(t[i]),t[i]=(t[i]+ans)%n+1;
            ans=0;
            for(int i=1;i<=tp;i++) 
                ans+=LCT.Access(t[i]);
            printf("%lld\n",ans);
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/l_0_forever_lf/article/details/80859568
今日推荐