2019HDU杭电多校-第三场 -部分题目Find the answer,Blow up the city

Find the answer

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6609
题意:
题意很简单,就是给定一个序列ai,和一个数m。对于a中的第i个位置,求i的前缀和中最少删去几个元素可以使其前缀和小于等于m。对于每一位ai,求出一个最小删除的数的数量。
思路:
比赛时写了二分+树状数组,自以为是nlogn算法应该能过,但是其实是nlognlogn的算法,T的明明白白(不明不白),所以问了大佬之后,离线从先往后维护一个权值线段树,每一个节点有左右区间l,r,还有目前这个区间的所有数的和,以及数的数量。查询时,由于我们需要前缀和小于m最少删除多少数,那么我们可以查询目前已经出现的数中,最多可以有多少数使他们的和小于等于m,然后用已经出现的数的数量减去查询结果就是最少删除的数。由于数的大小到1e9,所以急需要离散化。
刚学线段树的时候就写了一道权值线段树的题,当时还和权值树状数组比较,比赛时竟然没有想到,宛如zz。

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _fileout freopen("out.txt","w",stdout)
#define _filein freopen("in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret = 1;while(b){if (b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return (a*b)/gcd(a,b);}
struct segment_tree
{
    int l,r;
    int num;
    ll w;
}t[MAXN];
int a[MAXN],b[MAXN];
void build(int p,int l,int r)
{
    t[p].l=l;t[p].r=r;
    t[p].num=0;t[p].w=0ll;
    if(l==r)return;
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    return;
}
void change(int p,int tar)//更新线段树
{
    if(t[p].l==t[p].r){t[p].w+=b[tar];t[p].num++;return;}
    int mid=(t[p].l+t[p].r)/2;
    if(tar<=mid)change(p*2,tar);
    else change(p*2+1,tar);
    t[p].w=t[p*2].w+t[p*2+1].w;
    t[p].num=t[p*2].num+t[p*2+1].num;
    return;
}
int ask(int p,int val)//查询
{
    if(val>=t[p].w)return t[p].num;
    if(t[p].l==t[p].r) return min(t[p].num,val/b[t[p].l]);//注意这里需要取二者最小值。
    if(t[p*2].w>=val)return ask(p*2,val);
    else return t[p*2].num+ask(p*2+1,val-t[p*2].w);
}
int n,m;
int main(void)
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);b[i]=a[i];
        }
        sort(b+1,b+1+n);
        int len=unique(b+1,b+1+n)-b-1;
        // printf("len=%d\n",len);
        build(1,1,len);
        for(int i=1;i<=n;i++)
        {
            if(i==1){printf("0 ");}
            // for(int j=1;j<=13;j++)printf("j=%d num=%d w=%d\n",j,t[j].num,t[j].w);
            else printf("%d ",i-1-ask(1,m-a[i]));//因为只能删除前i-1个,所以是m-a[i]。
            int id=lower_bound(b+1,b+1+len,a[i])-b;//离散化出a[i]的下标
            // printf("id=%d\n",id);
            change(1,id);
        }
        printf("\n");
    }

    return 0;
}   

Blow up the city

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6604
题目大意:
给一个DAG(有向无环图),图中所有的出度为0的点都是重点。有q个查询,每次查询给两个起点。你可以破话一个点以及它周围所有的边,求有多少种破坏的方案,可以使这两个起点至少有一个点不能到达终点。
思路:
在打比赛的时候不知道有支配树这个东西,所以当时想的是先进性拓扑排序,并把对原图找个店,然后每次查询都要在拓扑序列中找到两个起点与重点之间的割点的数量。但是首先时间复杂度可能会爆,其次很多情况都会WA。
后来打完比赛看题解,基本是一个裸的支配树,支配树好像是近几年才有的一个树状结构,没了解过的可以参考一下这个链接:https://blog.csdn.net/qq_42785590/article/details/98105548
所以知道了支配树,基本上这道题就解决了。每次查询只需要在支配树上找两点的LCA,然后用两点的深度减去LCA的深度就是答案。时间复杂度:O(Tnlogn)
AC代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#define pb push_back
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=2e5+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
int head[MAXN],e[MAXN],nex[MAXN],o;
void add(int x,int y)
{
    e[++o]=y;
    nex[o]=head[x];head[x]=o;
}
int head1[MAXN],e1[MAXN],nex1[MAXN],o1;
void add1(int x,int y)
{
    e1[++o1]=y;
    nex1[o1]=head1[x];head1[x]=o1;
}
int head_zp[MAXN],nex_zp[MAXN],e_zp[MAXN],tot;
void add_zp(int x,int y)
{
    e_zp[++tot]=y;
    nex_zp[tot]=head_zp[x];head_zp[x]=tot;
}
int n,m;
int f[MAXN][20];
int d[MAXN];
int du[MAXN];
vector<int>v;
queue<int>q;
void init()
{
    for(int i=1;i<=max(m,n);i++)
    {
        head[i]=head1[i]=head_zp[i]=0;
        nex[i]=nex1[i]=nex_zp[i]=0;
        du[i]=0;
    }
    o=0;o1=0;tot=0;
    memset(f,0,sizeof(f));memset(d,0,sizeof(d));
    v.clear();
    while(!q.empty())q.pop();
}
void topo()
{
    int x;
    while(!q.empty())
    {
        x=q.front();
        q.pop();
        v.pb(x);
        for(int i=head[x];i;i=nex[i])
        {
            int y=e[i];
            du[y]--;
            if(!du[y])q.push(y);
        }
    }
}
int t;
int lca(int x,int y)
{
    if(d[x]>d[y])swap(x,y);
    for(int i=t;i>=0;i--)
        if(d[f[y][i]]>=d[x])y=f[y][i];
    // printf("x=%d y=%d\n",x,y);
    if(x==y)return x;
    for(int i=t;i>=0;i--)
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    return f[x][0];
}
int main()
{
    // _filein;_fileout;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(y,x);
            du[x]++;
            add1(x,y);
        }
        for(int i=1;i<=n;i++)if(!du[i])q.push(i);
        topo();
        int len=v.size();
        // for(int i=0;i<len;i++)printf("%d ",v[i]);
        // printf("\n");
        t=(int)(log(n)/log(2))+1;
        for(int i=0;i<len;i++)
        {
            int x=v[i];
            int mid=e1[head1[x]];
            for(int j=head1[x];j;j=nex1[j])
            {
                int y=e1[j];
                // printf("mid=%d y=%d\n",mid,y);
                // printf("d[mid]=%d d[y]=%d\n",d[mid],d[y]);
                mid=lca(mid,y);
            }
            add_zp(mid,x);
            // printf("mid=%d x=%d\n",mid,x);
            f[x][0]=mid;
            d[x]=d[mid]+1;
            for(int l=1;l<=t;l++)
            {
                f[x][l]=f[f[x][l-1]][l-1];
            }
        }
        // ok(0);
        // for(int i=1;i<=n;i++)
        // {
        //     for(int j=head_zp[i];j;j=nex_zp[j])
        //     {
        //         int y=e_zp[j];
        //         printf("%d %d\n",i,y);
        //     }
        // }
        // ok(1);
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d\n",d[x]+d[y]-d[lca(x,y)]);
        }

    }
    return 0;
}

代码常数还是很大的,跑了一秒多。不过还好没有卡常。
在这里插入图片描述总结: 最近真是越来越菜了,简直要菜哭了,每次碰到难一点儿的题就蒙蔽,比赛完了看一下题解,瞬间发现自己不是做过类似的,就是很裸的题,脑子真是越来越不够用了!定将加倍努力。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值