7.10总结

7.10总结

得分情况

估分:80+0+80

实际:0+0+40

心态爆炸

T1

题目大意

给出一颗有n个节点的无根树,每个节点是黑色,白色或灰色,删去一些边使剩余每个连通块中要么不含黑色节点,要么不含白色节点,求删去边的权值和最小

有T组数据

n<=300000;T<=5

明显是树形dp,但是我方程推错了

正解

事实上,对于每个节点,只需记录三种状态:

  1. 有0个黑点和任意个白点
  2. 有任意个黑点和0个白点
  3. 有任意个黑点和1个白点

随便转移就好了

T2

题目大意

给出一个n个点,m条边的无向连通图,第i个点的权值为ai。删去一些边使点1到n任意一条最短路径上至少有一条边被删除,删去每条边(x,y)的代价为a[x]或a[y]。

求最小的代价即最优方案是否唯一。

2<=n<=400,2<=m<=4000,无向图可能有重边,不超过5组数据。

正解

最小割裸题。

首先建出最短路图。我们只需要在最短路图上删去若干条边使1到n不连通即可。

但是边(x,y)的权值可能是a[x]也可能是a[y]怎么办呢?

把边拆成(x,z)与(z,y),(x,z)的流量为a[x],(z,y)的流量为a[y]即可

这样跑一下就会有40分的好成绩了

判断最优方案是否唯一就是研究最小割是否有唯一解。

这是本题重点。

如何判断最小割唯一性

先跑出残量网络,找出S集和T集。

(若从S走流量不为0的边能走到x,则x在S集;若从T跑 流量不为0的边 的反向弧 能走到x则x在y集)

枚举每一条边,若这条边连接的x,y分别在S集与T集,则这条边是必割边

若全部必割边的流量之和为最小割,则方案唯一。

扩展:判断最大流方案唯一性

如果残量网络中存在长度大于2的有向环,则方案不唯一

这时在环上增减流量可以得到不同解而方案不变。

T3

题目大意

对于一个整数序列,查询区间第k大数可以在O(logN)的时间内轻松完成。现在我们对这个问题进行推广。

考虑带重复数的集合(multiset)。定义在该类集合上的并操作“+”为两个集合的所有数不剔除重复得到的结果。比如,若A={1,2,2,3},B={2,3,4,4},则C={1,2,2,2,3,3,4,4}。对于一个给定序列A[1..N],定义A[x..y]为包含y-x+1个元素的集合{A[x],A[x+1],…,A[y]}。现在,给定整数序列A,你需要回答很多询问,其中第i个询问要求集合A[x[i,1]..y[i,1]]+A[x[i,2]..y[i,2]]+…+A[x[i,ki]..y[i,ki]]中第pi小的元素。

所有的测试点满足N ≤ 200 000,M ≤ 200 000,0 ≤ A[i] ≤ 1 000 000,ki ≤ 5。

保证1 ≤ x[i, j] ≤ N,1 ≤ y[i, j] ≤ N,1 ≤ pi ≤ ∑j(y[i, j] - x[i, j] + 1)。

比赛时建了主席树,但是只会二分然后枚举每一段,带两个节点进去,O(\(nlog^2n\))只有40分

正解

带十个节点进去,在主席树上面二分即可

O(\(nlogn\))

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

struct qy
{
    int ch[2],sum;
};

int n,m,i,j,k,p,tot,l,r,mid,ans;
int a[200005],root[200005],x[10],y[10];
int k1[15],k2[15];
qy tree[6000005];

void insert(int k1,int k2,int l,int r,int x)
{
    if (l==r)
    {
        tree[k2].sum++;
    }
    else
    {
        int mid=(l+r)/2;
        tree[k2].ch[0]=tree[k1].ch[0];
        tree[k2].ch[1]=tree[k1].ch[1];
        if (x<=mid)
        {
            tree[k2].ch[0]=++tot;
            tree[tot]=tree[tree[k1].ch[0]];
            insert(tree[k1].ch[0],tree[k2].ch[0],l,mid,x);
        }
        else
        {
            tree[k2].ch[1]=++tot;
            tree[tot]=tree[tree[k1].ch[1]];
            insert(tree[k1].ch[1],tree[k2].ch[1],mid+1,r,x);
        }
        tree[k2].sum=tree[tree[k2].ch[0]].sum+tree[tree[k2].ch[1]].sum;
    }
}

int query(int k,int l,int r,int x)//重点
{
    if (l==r) return l;
    else
    {
        int s=0,mid=(l+r)/2;
        for (int i=1;i<=k;i++)//把2k个节点的和一起计算
        s=s+tree[tree[k1[i]].ch[0]].sum-tree[tree[k2[i]].ch[0]].sum;
        if (s>=x)
        {
            for (int i=1;i<=k;i++)
            {
                k1[i]=tree[k1[i]].ch[0];
                k2[i]=tree[k2[i]].ch[0];
            }
            return query(k,l,mid,x);
        }
        else
        {
            for (int i=1;i<=k;i++)
            {
                k1[i]=tree[k1[i]].ch[1];
                k2[i]=tree[k2[i]].ch[1];
            }
            return query(k,mid+1,r,x-s);
        }
    }
}

int read()
{
    int sum=0;char ch=getchar();
    while ((ch<'0')||(ch>'9')) ch=getchar();
    while ((ch>='0')&&(ch<='9'))
    {
        sum=sum*10+ch-'0';
        ch=getchar();
    }
    return sum;
}

int main()
{
    freopen("read.in","r",stdin);
//  freopen("write.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        root[i]=++tot;
        insert(root[i-1],root[i],0,1000000,a[i]);
    }
    for (i=1;i<=m;i++)
    {
        k=read();
        p=read();
        for (j=1;j<=k;j++)
        x[j]=read(),y[j]=read();
        for (j=1;j<=k;j++)
        {
            k1[j]=root[y[j]];
            k2[j]=root[x[j]-1];
        }
        ans=query(k,0,1000000,p);
        printf("%d\n",ans);
    }
}

花絮

xzb下午没人叫起床

dyp帮忙打开屏幕,zzx帮忙打开C++,结果没被xc发现

xzb15:53才到教室

猜你喜欢

转载自www.cnblogs.com/leason-lyx/p/11165083.html