SDOI2015

SDOI一轮过去,觉得自己爆搜都会写残。。。好无奈。。。

先修课过去,觉得自己手速慢加各种爆搜残。。。好无奈。。。

day1T2

题目大意:一棵树,给出边的长度,每次操作改变一个点的信息(标记或取消标记),之后输出从一个点出发、走过所有标记点之后回到起始点的最小距离。

思路:一轮的时候只能弱弱的写暴力,下来听TA的题解,看TA各种ac。。。其实我们每次加入一个点,都要使他尽快并到已有的路线上去,这时候我们要找两个lca:所有原来点的lca1(也就是dfs序里最两边的标记点的lca),与这个点两边两个点(也就是dfs序中离这个点最近的两个标记点)的两个lca中离这个点较近的lca2(一开始理解错了这个lca,这里距离最近才能保证答案最小)。这两个lca一定是祖孙关系(可能是一个点)。然后就分类讨论一下:如果lca1是lca2的祖先,答案就是新加入点道lca2的距离;如果反过来,答案还要加上lca1到lca2的距离。这里的距离以及求lca所要用的信息都可以在建树的时候求出(第一次写了非递归的建树)。用一个线段树来维护一下dfs序中点被标记的情况。在插入和删除的时候,要注意修改和查询线段树的顺序。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 2100000000LL
#define proof 17
using namespace std;
struct use{
    int st,en,va;
}edge[200001]={0};
struct used{
    int hi;
    long long dis;
}dian[100001]={0};
struct uss{
    int maxp,minp,num;
}tree[400000];
int tot=0,next[200001]={0},point[100001]={0},bi[100001]={0},zhan[100001]={0},kk[100001]={0},cur[100001]={0},ff[100001][18]={0};
bool visit[100001]={false};
void add(int st,int en,int va)
{
    ++tot;next[tot]=point[st];point[st]=tot;
    edge[tot].st=st;edge[tot].en=en;edge[tot].va=va;
    ++tot;next[tot]=point[en];point[en]=tot;
    edge[tot].st=en;edge[tot].en=st;edge[tot].va=va;
}
void updata(int i)
{
    tree[i].maxp=max(tree[i*2].maxp,tree[i*2+1].maxp);
    tree[i].minp=min(tree[i*2].minp,tree[i*2+1].minp);
    tree[i].num=tree[i*2].num+tree[i*2+1].num;
}
void build(int i,int l,int r)
{
    int mid;
    if (l==r)
    {
        tree[i].maxp=-inf;tree[i].minp=inf;
        tree[i].num=0;return;
    }
    mid=(l+r)/2;
    build(i*2,l,mid);build(i*2+1,mid+1,r);
    updata(i);
}
void ins(int i,int l,int r,int x,int y)
{
    int mid;
    if (l==r)
    {
        tree[i].num+=y;
        if (tree[i].num) tree[i].maxp=tree[i].minp=l;
        else
        {
            tree[i].maxp=-inf;tree[i].minp=inf;
        }
        return;
    }
    mid=(l+r)/2;
    if (x<=mid) ins(i*2,l,mid,x,y);
    else ins(i*2+1,mid+1,r,x,y);
    updata(i);
}
int ask(int i,int l,int r,int ll,int rr,int kk)
{
    int mid,aaa;
    if (ll<=l&&r<=rr)
    {
        if (kk) return tree[i].maxp;
        else return tree[i].minp;
    }
    mid=(l+r)/2;
    if (kk) aaa=-inf;
    else aaa=inf;
    if (ll<=mid)
    {
        if (kk) aaa=max(aaa,ask(i*2,l,mid,ll,rr,kk));
        else aaa=min(aaa,ask(i*2,l,mid,ll,rr,kk));
    }
    if (rr>mid)
    {
        if (kk) aaa=max(aaa,ask(i*2+1,mid+1,r,ll,rr,kk));
        else aaa=min(aaa,ask(i*2+1,mid+1,r,ll,rr,kk));
    }
    return aaa;
}
int lca(int x,int y)
{
    int i,j;
    if (x==y) return x;
    if (dian[x].hi>dian[y].hi) swap(x,y);
    for (i=proof;i>=0;--i)
      if (dian[x].hi<=dian[ff[y][i]].hi)
          y=ff[y][i];
    if (x==y) return x;
    for (i=proof;i>=0;--i)
    {
        if (ff[x][i]!=ff[y][i])
        {
            x=ff[x][i];y=ff[y][i];
        }
    }
    return ff[x][0];
}
int main()
{
    int n,m,i,j,u,v,w,x,p1,p2,lca1,lca2,lca3;
    long long ans=0;
    scanf("%d%d",&n,&m);
    for (i=1;i<n;++i)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for (i=1;i<=n;++i) cur[i]=point[i];
    i=1;zhan[i]=1;u=1;bi[1]=1;visit[1]=true;
    while(i)
    {
        x=zhan[i];
        for (j=1;j<=proof;++j)
            ff[bi[x]][j]=ff[ff[bi[x]][j-1]][j-1];
        while (cur[x])
        {
            j=cur[x];cur[x]=next[cur[x]];
            if (!visit[edge[j].en])
            {
               ++i;++u;zhan[i]=edge[j].en;
               bi[edge[j].en]=u;dian[u].dis=dian[bi[x]].dis+(long long)edge[j].va;
               dian[u].hi=i-1;ff[u][0]=bi[x];
               visit[edge[j].en]=true;break;
            }
        }
        if (!cur[zhan[i]]) --i;
    }
    build(1,1,n);
    for (i=1;i<=m;++i)
    {
        scanf("%d",&j);
        j=bi[j];kk[j]=1-kk[j];u=j;
        if (tree[1].num==0)
        {
            printf("0\n");ins(1,1,n,j,1);continue;
        }
        if (tree[1].num<=2&&kk[j]==0)
        {
            printf("0\n");ins(1,1,n,j,-1);continue;
        }
        if (j>1) p1=ask(1,1,n,1,j-1,1);
        else p1=-inf;
        if (j<n) p2=ask(1,1,n,j+1,n,0);
        else p2=inf;
        if (kk[j])
        {
            lca1=lca(tree[1].minp,tree[1].maxp);
            ins(1,1,n,j,1);
            lca2=lca3=0;
            if (p1>-inf) lca2=lca(p1,j);
            if (p2<inf) lca3=lca(p2,j);
            if ((lca2==0)||(lca2!=0&&dian[lca2].dis<dian[lca3].dis)) lca2=lca3;
            if (dian[lca2].dis>=dian[lca1].dis)
                ans+=dian[j].dis-dian[lca2].dis;
            if (dian[lca2].dis<dian[lca1].dis)
                ans+=dian[j].dis-dian[lca2].dis+dian[lca1].dis-dian[lca2].dis;
        }
        else
        {
            ins(1,1,n,j,-1);
            lca1=lca(tree[1].minp,tree[1].maxp);
            lca2=lca3=0;
            if (p1>-inf) lca2=lca(p1,j);
            if (p2<inf) lca3=lca(p2,j);
            if ((lca2==0)||(lca2!=0&&dian[lca2].dis<dian[lca3].dis)) lca2=lca3;
            if (dian[lca2].dis>=dian[lca1].dis)
              ans-=(dian[j].dis-dian[lca2].dis);
            if (dian[lca2].dis<dian[lca1].dis)
              ans-=(dian[j].dis-dian[lca2].dis+dian[lca1].dis-dian[lca2].dis);
        }
        printf("%lld\n",ans*2);
    }
}

day2T1

题目大意:有n个敌人,m个武器,每种武器有特定的攻击敌人,伤害不同,求最短消灭所有敌人的时间。

思路:因为有小数,我们乘一个1000000,到整数上,然后二分答案,跑网络流(每种武器在二分时间的最多的伤害已知,如果到汇点满流,就是可行)。

注意longlong和int之间一些强转。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 1000000
#define linf 9223372036854775806LL
using namespace std;
struct use{
    int st,en;
    long long va;
}edge[10000]={0};
int a[100]={0},b[100]={0},tot,map[100][100],bb[100]={0},ba[100]={0},n,m,point[200]={0},next[10000]={0},
    gap[200]={0},cur[200]={0},dis[200]={0},pre[200]={0};
void add(int st,int en,long long va)
{
    ++tot;next[tot]=point[st];point[st]=tot;
    edge[tot].st=st;edge[tot].en=en;edge[tot].va=va;
    ++tot;next[tot]=point[en];point[en]=tot;
    edge[tot].st=en;edge[tot].en=st;edge[tot].va=0;
}
long long sap(int st,int en)
{
    int i,j,u;
    long long minn,ans=0;
    bool f=false;
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memset(pre,0,sizeof(pre));
    gap[0]=en-st+1;u=st;
    for (i=st;i<=en;++i) cur[i]=point[i];
    while(dis[st]<en-st+1)
    {
        f=false;
        for (i=cur[u];i;i=next[i])
        {
            if (edge[i].va&&dis[edge[i].en]+1==dis[u])
            {
                f=true;cur[u]=i;break;
            }
        }
        if (f)
        {
            pre[edge[i].en]=i;u=edge[i].en;
            if (u==en)
            {
                minn=linf;
                for (i=en;i!=st;i=edge[pre[i]].st)
                    minn=min(minn,edge[pre[i]].va);
                ans+=minn;
                for (i=en;i!=st;i=edge[pre[i]].st)
                {
                    edge[pre[i]].va-=minn;
                    edge[pre[i]^1].va+=minn;
                }
                u=st;
            }
        }
        else
        {
            --gap[dis[u]];
            if (!gap[dis[u]]) return ans;
            minn=en-st+1;
            for (i=point[u];i;i=next[i])
              if (edge[i].va&&dis[edge[i].en]<minn) minn=dis[edge[i].en];
            cur[u]=point[u];
            dis[u]=minn+1;++gap[dis[u]];
            if (u!=st) u=edge[pre[u]].st;
        }
    }
    return ans;
}
bool judge(long long tim,int en)
{
    int i,j;
    long long sum=0,k;
    tot=1;
    memset(point,0,sizeof(point));
    memset(next,0,sizeof(next));
    for (i=1;i<=m;++i)
        add(1,bb[i],tim*(long long)b[i]);
    for (i=1;i<=m;++i)
      for (j=1;j<=n;++j)
          if (map[i][j]) add(bb[i],ba[j],linf);
    for (j=1;j<=n;++j)
    {
      add(ba[j],en,(long long)inf*(long long)a[j]);
      sum+=(long long)inf*(long long)a[j];
    }
    k=sap(1,en);
    if (k==sum) return true;
    else return false;
}
int main()
{
    int i,j,sum=0,minn,en;
    long long ll,rr,mid;
    double ans;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
    {
       scanf("%d",&a[i]);
       sum+=a[i];
       ba[i]=m+i+1;
    }
    minn=2100000000;
    for (j=1;j<=m;++j)
    {
        scanf("%d",&b[j]);
        minn=min(minn,b[j]);
        bb[j]=j+1;
    }
    en=2+m+n;
    for (i=1;i<=m;++i)
      for (j=1;j<=n;++j)
        scanf("%d",&map[i][j]);
    ll=0;rr=(long long)(sum/minn)*(long long)inf;
    while(ll<rr)
    {
        mid=(ll+rr)/2;
        if (judge(mid,en)) rr=mid;
        else ll=mid+1;
    }
    ans=ll*1.0/inf;
    printf("%.6f\n",ans);
}

时间: 2024-11-05 16:09:26

SDOI2015的相关文章

BZOJ 3994: [SDOI2015]约数个数和

3994: [SDOI2015]约数个数和 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 898  Solved: 619[Submit][Status][Discuss] Description 设d(x)为x的约数个数,给定N.M,求   Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组数. 接下来的T行,每行两个整数N.M. Output T行,每行一个整数,表示你所求的答案. Sample Input 2 7

BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

【BZOJ3991】[SDOI2015]寻宝游戏 树链的并+set

[BZOJ3991][SDOI2015]寻宝游戏 Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止.小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程.但是这个游戏中宝物经常变化,有时某个村庄中会突

【BZOJ 3994】3994: [SDOI2015]约数个数和(莫比乌斯反演)

3994: [SDOI2015]约数个数和 Description 设d(x)为x的约数个数,给定N.M,求   Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组数. 接下来的T行,每行两个整数N.M. Output T行,每行一个整数,表示你所求的答案. Sample Input 2 7 4 5 6 Sample Output 110 121 HINT 1<=N, M<=50000 1<=T<=50000 Source Round 1 感谢yts199

[SDOI2015][BZOJ3991] 寻宝游戏|set|dfs序|虚树|树上倍增LCA

3991: [SDOI2015]寻宝游戏 Time Limit: 40 Sec  Memory Limit: 128 MBSubmit: 427  Solved: 212[Submit][Status][Discuss] Description 小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直

BZOJ 3995 Sdoi2015 道路修建 线段树

题目大意:给定一个2*n的网格图,多次改变某条边的权值或询问y坐标在[l,r]中的2*(r-l+1)个点的MST 这真是一道好题= = 我们用线段树维护每个区间内的MST 然后考虑合并 合并两个区间 我们会加入两条边 这样一定会形成一个环 切掉环上最大边 这题没了 然后就是一坨乱七八糟的细节讨论= = 首先最大边一定在图中的彩色部分内 绿色部分可以O(1)求 我们需要维护的是红色和蓝色部分 然后如果切掉的边是横边或者切掉的竖边不是区间唯一的竖边(如图中蓝色竖边) 那么红框和蓝框直接用左右区间的即

【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

[BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个.小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi.另外,小C认为这个问题的答案可能

【BZOJ】【3991】【SDOI2015】寻宝游戏

dfs序 我哭啊……这题在考试的时候(我不是山东的,CH大法吼)没想出来……只写了50分的暴力QAQ 而且苦逼的写的比正解还长……我骗点分容易吗QAQ 骗分做法: 1.$n,m\leq 1000$: 直接找一个关键点做根进行深搜,算出其他关键点都与root连通的最小边权和,再×2 2.一条链的情况:用set维护序列中哪些点选了,然后ans=(sum[tail]-sum[head])*2; 其中sum[i]表示从序列首到第 i 个的边长之和……嗯就是前缀和优化一下= =取首和尾这一段的Len之和

【BZOJ3993】[SDOI2015]星际战争 二分+最大流

[BZOJ3993][SDOI2015]星际战争 Description 3333年,在银河系的某星球上,X军团和Y军团正在激烈地作战.在战斗的某一阶段,Y军团一共派遣了N个巨型机器人进攻X军团的阵地,其中第i个巨型机器人的装甲值为Ai.当一个巨型机器人的装甲值减少到0或者以下时,这个巨型机器人就被摧毁了.X军团有M个激光武器,其中第i个激光武器每秒可以削减一个巨型机器人Bi的装甲值.激光武器的攻击是连续的.这种激光武器非常奇怪,一个激光武器只能攻击一些特定的敌人.Y军团看到自己的巨型机器人被X