SDOI2015

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

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

 

day1T1

题目大意:第i(1<=i<=n)种操作为将序列从左到右划分为2^{n-i+1}段,每段恰好包括2^{i-1}个数,然后整体交换其中两段。每种操作只能用一次,问有多少种不同的交换方式。

思路:每次出现一种操作个数为x的操作序列,那么都有x!种相应的变换,因为交换这个序列中任意两个,都一定有对应的操作方法。因此我们枚举出本质不同的操作序列就可以了。从小的操作开始做,每次都分成2^i个数的2^(n-i)个块。找到不匹配的块(不是连续递增的),保存一下。如果没有,就不用进行这种操作;如果有一个,就看这个块先半部分和后半部分交换后能否满足条件;如果有两个,就有四种交换方法,分情况判断一下。根据相应的变换交换这半个块的开头和结尾的元素就可以了(之后每次的块都扩大两倍,所以不会用到中间的元素,而从小到大操作有保证了块内有序)。

#include<iostream>

#include<cstdio>

#include<algorithm>

using namespace std;

int a[5000]={0},n;

long long ans,jie[13]={0};

void swa(int i,int j,int len)

{

    int k;

    for (k=0;k<len;++k) swap(a[i+k],a[j+k]);

}

void dfs(int i,int sum)

{

    int zhan[3]={0},totz=0,j,k,len;

    if (i==n+1)

    {

        ans+=jie[sum];return;

    }

    len=1<<i;

    for (j=1;j<=1<<n;j+=len)

    {

        if (a[j+len/2-1]+1!=a[j+len/2])

        {

            if (totz==2) return;

            else zhan[++totz]=j;

        }

    }

    if (totz==0)

        dfs(i+1,sum);

    if (totz==1)

    {

        j=zhan[totz];

        if (a[j]==a[j+len-1]+1)

        {

            swap(a[j],a[j+len/2]);

            if (len>2) swap(a[j+len/2-1],a[j+len-1]);

            dfs(i+1,sum+1);

            swap(a[j],a[j+len/2]);

            if (len>2) swap(a[j+len/2-1],a[j+len-1]);

        }

    }

    if (totz==2)

    {

        j=zhan[1];k=zhan[2];

        if (a[j+len/2-1]+1==a[k+len/2]&&a[k+len/2-1]+1==a[j+len/2])

        {

            swap(a[j],a[k]);

            if (len>2) swap(a[j+len/2-1],a[k+len/2-1]);

            dfs(i+1,sum+1);

            swap(a[j],a[k]);

            if (len>2) swap(a[j+len/2-1],a[k+len/2-1]);

            swap(a[j+len/2],a[k+len/2]);

            if (len>2) swap(a[j+len-1],a[k+len-1]);

            dfs(i+1,sum+1);

            swap(a[j+len/2],a[k+len/2]);

            if (len>2) swap(a[j+len-1],a[k+len-1]);

        }

        if (a[j+len-1]+1==a[k+len/2]&&a[j+len/2-1]+1==a[k])

        {

            swap(a[j+len/2],a[k]);

            if (len>2) swap(a[j+len-1],a[k+len/2-1]);

            dfs(i+1,sum+1);

            swap(a[j+len/2],a[k]);

            if (len>2) swap(a[j+len-1],a[k+len/2-1]);

        }

        if (a[k+len-1]+1==a[j+len/2]&&a[k+len/2-1]+1==a[j])

        {

            swap(a[j],a[k+len/2]);

            if (len>2) swap(a[j+len/2-1],a[k+len-1]);

            dfs(i+1,sum+1);

            swap(a[j],a[k+len/2]);

            if (len>2) swap(a[j+len/2-1],a[k+len-1]);

        }

    }

}

int main()

{

    int i,j;

    scanf("%d",&n);

    for (i=1;i<= 1<<n;++i) scanf("%d",&a[i]);

    jie[0]=(long long)1;

    for (i=1;i<=n;++i) jie[i]=jie[i-1]*(long long)i;

    dfs(1,0);

    printf("%I64d\n",ans);

}
View Code

 

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);

    }

}
View Code

 

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);

}
View Code

 

你可能感兴趣的:(sd)