[20181125]四校联考

商人(business)

[题意]

n个节点的树上有m条简单路径,对于每条路径,求出有多少条路径与它相交。

我的做法看上去就很low。

[题解]

  • 开两个树状数组,分别记下lca在当前点之上的和在当前点之下的。

  • 对于后者,用dfs序就可以计算子树内的value和
  • 对于前者,我们把询问按照lca的dep值排序来做

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define swap(x,y) (x^=y^=x^=y) 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 200005
int n,m,hr[MN],en;
struct ed{int to,nex;}e[MN<<1];
inline void ins(int f,int t)
{
    e[++en]=(ed){t,hr[f]};hr[f]=en;
    e[++en]=(ed){f,hr[t]};hr[t]=en;
}
struct Ques{int a,b,id,LCA;}q[MN];
int ans[MN];
int mx[MN],siz[MN],dep[MN],fa[MN],top[MN],dfn[MN],r[MN],dind;
inline bool cmp(Ques o,Ques oo){return dep[o.LCA]<dep[oo.LCA];}
void dfs1(int x,int f,int d)
{
    register int i;
    fa[x]=f;dep[x]=d;siz[x]=1;
    for(i=hr[x];i;i=e[i].nex)if(f^e[i].to)
    {
        dfs1(e[i].to,x,d+1);siz[x]+=siz[e[i].to];
        siz[e[i].to]>siz[mx[x]]?mx[x]=e[i].to:0;
    }
}
void dfs2(int x,int f,int tp)
{
    top[x]=tp;dfn[x]=++dind;
    if(mx[x]) dfs2(mx[x],x,tp);
    register int i;
    for(i=hr[x];i;i=e[i].nex)
        if((f^e[i].to)&&(e[i].to^mx[x]))
            dfs2(e[i].to,x,e[i].to);
    r[x]=dind;
}
int lca(int x,int y)
{
    for(;top[x]^top[y];)dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
    return dep[x]<dep[y]?x:y;
}
/*
namespace mini
{
    
    inline bool Cross(int x,int y)
    {
        int lcax=q[x].LCA,lcay=q[y].LCA;
        if(dep[lcax]>dep[lcay]) swap(x,y),swap(lcax,lcay);
        return lcay==lca(lcay,q[x].a)||lcay==lca(lcay,q[x].b); 
    }
    void solve()
    {
        register int i,j;
        for(i=1;i<n;i++) j=read(),ins(j,read());dfs1(1,0,1);dfs2(1,0,1);
        for(i=1;i<=m;i++) q[i].a=read(),q[i].b=read(),q[i].a>q[i].b?swap(q[i].a,q[i].b):0;
        for(i=1;i<=m;i++) q[i].LCA=lca(q[i].a,q[i].b);
        for(i=1;i<m;i++)for(j=i+1;j<=m;j++) if(Cross(i,j)) ans[i]++,ans[j]++;
        for(i=1;i<=m;i++) printf("%d\n",ans[i]);
        return;
    }
}*/
namespace Normal
{
    #define lowbit(x) (x&(-x))
    int ht[MN],tt[MN];
    inline void hC(int x,int val){for(;x<=n;x+=lowbit(x)) ht[x]+=val;}
    inline void tC(int x,int val){for(;x<=n;x+=lowbit(x)) tt[x]+=val;}
    inline int hG(int x){int res=0;for(;x;x-=lowbit(x)) res+=ht[x];return res;}
    inline int tG(int x){int res=0;for(;x;x-=lowbit(x)) res+=tt[x];return res;}
    int llca(int x,int y)
    {
        int res=0;
        for(;top[x]^top[y];)
        {
            if(dep[top[x]]>dep[top[y]])
            {
                res+=tG(dfn[x])-tG(dfn[top[x]]-1);
                x=fa[top[x]];
            }
            else
            {
                res+=tG(dfn[y])-tG(dfn[top[y]]-1);
                y=fa[top[y]];
            }
        }
        if(dep[x]>dep[y]) res+=tG(dfn[x])-tG(dfn[y]-1);
        else res+=tG(dfn[y])-tG(dfn[x]-1);
        return res;
    }
    void solve()
    {
        register int i,j;
        for(i=1;i<n;i++) j=read(),ins(j,read());
        dfs1(1,0,1);dfs2(1,0,1);
        for(i=1;i<=m;i++) q[i].a=read(),q[i].b=read(),q[i].a>q[i].b?swap(q[i].a,q[i].b):0;
        for(i=1;i<=m;i++) q[i].LCA=lca(q[i].a,q[i].b),q[i].id=i,tC(dfn[q[i].LCA],1);
        std::sort(q+1,q+m+1,cmp);
        for(i=1,j=1;i<=m;i++)
        {
            while(dep[q[j].LCA]<dep[q[i].LCA]) hC(dfn[q[j].a],1),hC(dfn[q[j].b],1),j++;
            ans[q[i].id]=hG(r[q[i].LCA])-hG(dfn[q[i].LCA]-1);
            ans[q[i].id]+=llca(q[i].a,q[i].b);
        }
        for(i=1;i<=m;i++) printf("%d\n",ans[i]-1);
    }
} 
int main()
{
    freopen("business.in","r",stdin);
    freopen("business.out","w",stdout);
    n=read();m=read();
    Normal::solve();
    return 0;
}

当然,有清楚得多的做法:

  • 如果两条链相交,他们相交的点数减相交的边数等于 1,否则等于 0。对每条链将其链上的点加点权 1,边加边权-1,那么对一条链求链上权值和就可以知道这条链与多少条链有交,用求 LCA 和树上差分等技巧优化加权值以及查权值和即可,时间复杂度?(?)?(?log?)




栅栏(barrier) 

[题意]

给定一个数组,求有多少个排列\(P_i\) 满足

\[\sum_{i=2}^{n}|?_{p_i}−?_{p_{i-1}}|\leq l\]

[题解]

  • 我们考虑一开始排列所有的数都为0,从小到大的将\(a_i\)扔进去,每次我们把所有还未赋值的都赋为\(a_i\)
  • 这样,当我们更新到\(a_{i+1}\)时,原式中的相邻差之和增大 (a[i+1]-a[i])(2*k+2-l),其中,k表示的是当前排列中有k对相邻数之间还要插入数字,l表示序列的两端中有几个还要插入数字,\(l\leq 2\)
  • 于是我们设计状态f[i][j][k][l]表示,前i个数已经加入,当前的相邻差之和为j,k、l的定义如上
  • 状态的转移略显麻烦,主要是要考虑到所有的情况

时间复杂度\(?(?^2?)\)



#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define swap(x,y) (x^=y^=x^=y)
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 105
#define MM 1005
#define mod 1000000007
int n,lm,a[MN];
ll f[MN][MM][MN][3],ans;
inline void add(ll &x,ll y){x+=y;x%=mod;}
int main()
{
    freopen("barrier.in","r",stdin);
    freopen("barrier.out","w",stdout);
    n=read();lm=read();
    register int i,j,k,l;
    for(i=1;i<=n;++i) a[i]=read();
    std::sort(a+1,a+n+1);
    if(n==1) return 0*puts("1");
    f[1][0][0][0]=1;f[1][0][0][1]=2;
    for(i=1;i<n;++i)for(j=0;j<=lm;++j)for(k=0;k<=i;++k)for(l=0;l<3;++l)if(f[i][j][k][l]){
        if(k==0&&l==2) continue;
        int J=j+(a[i+1]-a[i])*(k*2+2-l);if(J>lm) continue;
        if(l<2)add(f[i+1][J][k][l+1],f[i][j][k][l]*(2-l));
        if(l<2)add(f[i+1][J][k+1][l+1],f[i][j][k][l]*(2-l));
        add(f[i+1][J][k+1][l],f[i][j][k][l]*(k+2-l));
        if(k>0)add(f[i+1][J][k-1][l],f[i][j][k][l]*(k));
        add(f[i+1][J][k][l],f[i][j][k][l]*(k*2+2-l));
    }
    for(i=0;i<=lm;++i) add(ans,f[n][i][0][2]);
    return 0*printf("%d",ans);
}




地铁(subway) 

[问题描述]

小 R 在 X 城旅游,X 城有编号为1到?的?个地铁站,还有编号为1到?的?条地铁线,其中?号地铁线从\(?_{?,0}\)出发依次经过 \(?_{?,1},?_{?,2},…,?_{?,??}\),?号线从\(?_{?,?−1}\)前往 \(?_{i,?}\)需要花费\(?_{?,?}\)的时间,线路都是单向的。

小 R 现在要从1号站乘地铁前往?号站,他可以任意换乘。他希望自己乘坐地铁的时间之和尽量小,并且在此基础上,他不希望换乘地太频繁,他定义每次乘坐地铁的舒适度为上车到下车所经过时间的平方,他希望舒适度之和尽量大。

[输入格式]

第一行两个正整数?,?

接下来?行,每行先给出一个正整数??,接下来2?? + 1个正 整数,依次分别为\(?_{?,0},?_{?,1},?_{?,1},?_{?,2},?_{?,2},…,?_{?,??},?_{?,??}\)

保证从1号站出发能到达?号站。

[输出格式]

输出一行两个整数,分别表示最小乘坐时间以及在乘坐时间+最小时最大的舒适度之和。

[题解]

  • 首先是最小乘坐时间,把地铁线路拆成一段一段建图跑最短路即可。

    发现std中是用线段树求最短路的,感觉太巨了

  • 对于第二问,我们考虑 DP,用?[?]表示从1走到?路程最短下最大的舒适度之和,用???[?]表示 1 到?的最短路,\(?_{?,?}\)表示\(?_{?,?}\)的前缀和,那么如果\(?_{?,?}-T_{?,?} = ???[?_{i,?}] − ???[?_{?,?}]\), 我们就可以用\(?[?_{?,?}] + (?_{?,?} − ?_{?,?})^2\)更新⁡\(?[?_{?,?}]\),我们可以按\(???[?]\)从小到大的顺序依次计算出每个\(?[?]\)。不难注意到,这个转移式可以用斜率优化来加速计算,我们在 DP 的同时对每一段可能转移过来的线路开一个队列维护斜率优化需要的信息即可。



#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
char B[1<<25],*S=B;
inline int read()
{
    int x;char c;
    while((c=*S++)<'0'||c>'9');
    for(x=c-'0';(c=*S++)>='0'&&c<='9';)x=x*10+c-'0';
    return x;
}
#define MN 2000005
#define inf (2000000000)
#define N 1048576
std::vector<int> v[MN],w[MN],u[MN],q[MN];
std::vector<std::pair<int,int> >p[MN];
int d[MN],qn;
ll f[MN];
struct node{int x,f;}t[MN<<1];
inline node min(const node&o,const node&oo){return o.x<oo.x?o:oo;}
inline void rw(int k,int x){for(t[k+=N].x=x;k>>=1;)t[k]=min(t[k<<1],t[k<<1|1]);}
inline ll sqr(ll x){return x*x;}
inline double cal(int x,int y){return (double)(sqr(d[x])+f[x]-sqr(d[y])-f[y])/(d[x]-d[y]);}
int main()
{
    freopen("subway.in","r",stdin);
    freopen("subway.out","w",stdout);
    register int n,m,i,j,x,y,z,k;
    fread(B,1,1<<25,stdin);
    n=read(),m=read();
    for(i=1;i<=m;++i)
    {
        j=read();v[i].push_back(read());
        while(j--) w[i].push_back(read()),v[i].push_back(read());
        for(j=0;j<v[i].size();++j)
            p[v[i][j]].push_back(std::make_pair(i,j));
        u[i].resize(v[i].size());
    }
    for(i=1;i<N+N;++i) t[i].x=inf;
    for(i=1;i<=n;++i) d[t[i+N].f=i]=inf;
    for(rw(1,d[1]=0);t[1].x!=inf;)
    {
        rw(x=t[1].f,inf);
        for(i=0;i<p[x].size();++i)
        {
            y=p[x][i].first;z=p[x][i].second;
            k=u[y][z]=z&&d[v[y][z-1]]+w[y][z-1]==d[x]?u[y][z-1]:++qn;
            while(q[k].size()>1&&cal(q[k].back(),q[k][q[k].size()-2])<2*d[x])q[k].pop_back();
            if(q[k].size())f[x]=max(f[x],f[q[k].back()]+sqr(d[x]-d[q[k].back()]));
        }
        for(i=0;i<p[x].size();++i)
        {
            y=p[x][i].first;z=p[x][i].second;k=u[y][z];
            while(q[k].size()>1&&cal(x,q[k].back())>cal(q[k].back(),q[k][q[k].size()-2]))q[k].pop_back();
            q[k].push_back(x);
            if(z+1<v[y].size()&&d[x]+w[y][z]<d[v[y][z+1]])
                rw(v[y][z+1],d[v[y][z+1]]=d[x]+w[y][z]);
        }
    }
    printf("%d %lld",d[n],f[n]);
}




Blog来自PaperCloud,未经允许,请勿转载,TKS!

转载于:https://www.cnblogs.com/PaperCloud/p/10023963.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值