2017/10

题目编号是我瞎搞的

T1

因为数据范围很小
我还以为是折半搜索

没想到是个n3的DP

不难看出
跳楼的高度是单调的情况下才可能是最优
这样就先把高度排序
转移方程
dp[i][j] = min ( dp[i-1][k] + b[j].h - b[k].h ) + b[j].c ( k∈1~j-1 )
i表示跳了几次楼
j表示现在在哪座楼上
dp[i][j]表示跳i次楼到达j楼需要的最小花费

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
using namespace std;

struct nico
{
    int c,h;
}b[55];
int dp[55][55],n,t;

bool comp(nico a,nico b)
{
    return a.h<b.h;
}

int main()
{
    freopen("meet.in","r",stdin);
    freopen("meet.out","w",stdout);

    int i,j,k;

    scanf("%d",&n);

    for(i=1;i<=n;i++) scanf("%d",&b[i].c);
    for(i=1;i<=n;i++) scanf("%d",&b[i].h);

    scanf("%d",&t);

    sort(b+1,b+n+1,comp);   
    memset(dp,127,sizeof(dp));

    for(i=1;i<=n;i++) dp[0][i]=b[i].c;

    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
        {
            for(k=1;k<j;k++)
                dp[i][j]=min(dp[i][j],dp[i-1][k]+b[j].h-b[k].h);
            dp[i][j]+=b[j].c;
        }

    for(i=n;i>=0;i--)
        for(j=1;j<=n;j++)
            if(dp[i][j]<=t)
            {
                printf("%d",i+1);
                return 0;
            }

    printf("0");
    return 0;
}

T2

这题什么算法什么数据结构都没考

设原数列为a
两两相加后形成的数列为b
当场倒是想出来了
把a b升序排序
b1一定是a1+a2
b2一定是a1+a3

然后就不知道怎么办了
打了个暴力了事

实际上
再往后想的话
就会发现
我们并不知道a2+a3是多少
但如果a2+a3确定下来
a1 a2 a3就都能确定了
a1 a2 a3都确定下来以后
b数列中除去a1+a2 a1+a3 a2+a3
剩下的最小的一定是a1+a4
重复此过程

关于字典序
a2+a3从小到大枚举
这就意味着构造出的a1是从大到小的
又因为a1不可能重复
所以答案字典序可以确定为降序

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#define LL long long
using namespace std;

int n,m,cnt,s[45005],r[305],ans[305][305];
bool use[45005];

void check(int p)
{
    int i,j,k,tmp,px,pos;

    if((s[1]+s[2]+s[p])%2) return;

    memset(use,0,sizeof(use));

    r[1]=(s[1]+s[2]+s[p])/2-s[p];
    r[2]=s[1]-r[1];
    r[3]=s[2]-r[1];
    use[1]=1;use[2]=1;use[p]=1;

    for(i=4,j=3;i<=n;i++)
    {
        while(j<=m&&use[j]) j++;
        if(j>m) return;

        r[i]=s[j]-r[1];
        use[j]=1;

        for(k=2;k<i;k++)
        {
            if(r[k]>r[i]) return;

            tmp=r[k]+r[i];

            pos=lower_bound(s+1,s+m+1,tmp)-s;
            if(s[pos]!=tmp) return;

            px=pos;
            while((px<=m)&&(use[px])&&(s[px]==s[pos])) px++;
            if((px>m)||(use[px])||(s[px]!=s[pos])) return;

            use[px]=1;
        }
    }

    cnt++;
    for(i=1;i<=n;i++)
        ans[cnt][i]=r[i];
}


int main()
{
    freopen("city.in","r",stdin);
    freopen("city.out","w",stdout);

    int i,j;

    scanf("%d",&n);

    m=n*(n-1)/2;
    for(i=1;i<=m;i++) scanf("%d",&s[i]);
    sort(s+1,s+m+1);

    for(i=3;i<=m;)
    {
        check(i);

        j=i;
        while(j<=m&&s[j]==s[i]) j++;
        i=j;
    }   

    printf("%d\n",cnt);

    for(i=1;i<=cnt;i++)
    {
        for(j=1;j<=n;j++)
            printf("%d ",ans[i][j]);
        printf("\n");
    }

    return 0;
}

T3

看起来完全不会做
看了别人的写法
线段树还有这种操作

因为k<=10
所以只需要记录前十大
加了个重载运算符
合并的时候会简便很多

因为线段树常数很大
还以为会T

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
#define MOD 1000000007
using namespace std;

int n,m,num[100005],lazy[400005];

struct nico
{
    int res[11];

    nico operator + (const nico &b)const
    {
        nico z;
        int p1=0,p2=0,i;

        for(i=0;i<10;i++)
        {
            if(res[p1]>b.res[p2]) z.res[i]=res[p1++];
            else z.res[i]=b.res[p2++];
        }

        return z;   
    }
}tree[400005];

void build(int l,int r,int p)
{
    if(l==r) 
    {
        tree[p].res[0]=num[l];  
        return;
    }

    int mid=(l+r)>>1;

    build(l,mid,p<<1);
    build(mid+1,r,(p<<1)|1);
    tree[p]=tree[p<<1]+tree[(p<<1)|1];
}

void color(int p,int c)
{
    int i;

    lazy[p]+=c;

    for(i=0;i<10;i++)
        if(tree[p].res[i]) tree[p].res[i]+=c;
}

void pushdown(int p)
{
    if(lazy[p])
    {
        color(p<<1,lazy[p]);
        color((p<<1)|1,lazy[p]);
        lazy[p]=0;
    }
}
nico ask(int l,int r,int L,int R,int p)
{
    if(L<=l&&R>=r) return tree[p];

    int mid=(l+r)>>1;
    pushdown(p);

    if(R<=mid) return ask(l,mid,L,R,p<<1);
    if(L>mid) return ask(mid+1,r,L,R,(p<<1)|1);
    if(L<=mid&&R>mid) return ask(l,mid,L,mid,p<<1)+ask(mid+1,r,mid+1,R,(p<<1)|1);   
}



void add(int l,int r,int L,int R,int p,int c)
{
    if(L<=l&&R>=r) 
    {
        color(p,c);
        return;
    }

    int mid=(l+r)>>1;
    pushdown(p);
    if(R<=mid) add(l,mid,L,R,p<<1,c);
    if(L>mid) add(mid+1,r,L,R,(p<<1)|1,c);
    if(L<=mid&&R>mid) 
    {
        add(l,mid,L,mid,p<<1,c);
        add(mid+1,r,mid+1,R,(p<<1)|1,c);            
    }

    tree[p]=tree[p<<1]+tree[(p<<1)|1];  
}

int main()
{
    freopen("noname.in","r",stdin);
    freopen("noname.out","w",stdout);

    int i,opt,l,r,p;
    nico ans;

    scanf("%d%d",&n,&m);

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

    build(1,n,1);
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&opt,&l,&r,&p);

        if(opt==0) 
        {
            if(r-l+1<p) printf("-1\n");
            else 
            {
                ans=ask(1,n,l,r,1);
                printf("%d\n",ans.res[p-1]);
            }
        }

        if(opt==1) add(1,n,l,r,1,p);
    }

    return 0;
}

T4

打了个60分暴力
其实出题人可能想给80分
但是那20分没想出来怎么写

40%直接枚举就行
还有20%
因为保证所有数都小于1023
所以开两个桶
一个桶记原数
另一个记结果

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
#define MOD 1000000007
using namespace std;

int n,k,i,num[2005],p[10005],tot,ans,tmp,j,t,sum[2005];

bool comp(int a,int b)
{
    return a>b;
}

int main()
{
    freopen("war.in","r",stdin);
    freopen("war.out","w",stdout);

    scanf("%d%d",&n,&k);

    if(n<=100)
    {
        for(i=1;i<=n;i++)
            scanf("%d",&num[i]);

        for(i=1;i<=n;i++)
            for(j=i+1;j<=n;j++)
            {
                tot++;
                p[tot]=num[i]^num[j];
            }

        sort(p+1,p+tot+1,comp);
        for(i=1;i<=k;i++) ans=(ans+p[i])%MOD;
    }

    else
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&tmp);
            num[tmp]++;         
        }

        for(i=0;i<=1023;i++)
            if(num[i])
                for(j=i;j<=1023;j++)
                    if(num[j])
                    {
                        if(j==i) t=num[j]-1;
                        else t=num[j];

                        tmp=i^j;
                        sum[tmp]+=num[i]*t;
                    }                   

        for(i=1024;i>=0&&k>0;i--)
        {
            k-=sum[i];

            if(k<0) ans=(ans+(k+sum[i])*i)%MOD;
            else ans=(ans+sum[i]*i)%MOD;
        }
    }

    printf("%d",ans);
    return 0;
}

T5

这个题
太 抽 象 了

Kruscal求最大生成树
在并查集合并时
把原本的一个根连向另一个根改成两个根都连向一个新建的节点
并把当前正在处理的边的权值赋给这个新节点做点权
这样形成的结构会是一棵树
点u的答案
大致上是树的根到自己的路径上
相邻两个节点的子树叶节点数的平方和
需要注意
父子两个节点权值相同的情况
这个部分需要特殊处理

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<climits>
#include<queue>
#include<ctime>
#define LL long long
#define MOD 1000000007
using namespace std;

struct nico
{
    int u,v,l;
}e[500005];
int n,m,f[500005],val[500005],size[500005],son[500005][2];
LL ans[500005];

int read()
{
    char ch=getchar();
    int x=0;
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) {x=x*10+(ch-'0');ch=getchar();}
    return x;
}

bool comp(nico a,nico b)
{
    return a.l>b.l;
}

int find(int x)
{
    if(f[x]==x) return x;
    f[x]=find(f[x]);
    return f[x];
}

void dfs(int p,int fath,LL cnt)
{
    LL tmp=0;
    if(fath)
    {
        if(val[p]==val[fath]) size[p]=size[fath];
        else tmp=1ll*(size[fath]-size[p])*(size[fath]-size[p]);
    }

    if(son[p][0])
    {
        dfs(son[p][0],p,cnt+tmp);
        dfs(son[p][1],p,cnt+tmp);
    }
    else ans[p]=cnt+tmp;
}

int main()
{

    freopen("car.in","r",stdin);
    freopen("car.out","w",stdout);

    int i,newp,uf,vf;

    n=read();
    m=read();

    for(i=1;i<=m;i++)
    {
        e[i].u=read();
        e[i].v=read();
        e[i].l=read();
    }   
    sort(e+1,e+m+1,comp);

    for(i=1;i<=n;i++) f[i]=i;
    for(i=1;i<=n;i++) size[i]=1;
    newp=n;

    for(i=1;i<=m;i++)
    {
        uf=find(e[i].u);
        vf=find(e[i].v);

        if(uf!=vf)
        {
            newp++;
            f[uf]=newp;f[vf]=newp;
            f[newp]=newp;
            size[newp]=size[uf]+size[vf];
            son[newp][0]=uf;son[newp][1]=vf;
            val[newp]=e[i].l;
        }
    }

    dfs(newp,0,0);
    for(i=1;i<=n;i++) printf("%lld ",ans[i]);

    return 0;
}

T6

离散化
然后开桶存起来
注意正反面相同的要特殊处理

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
using namespace std;

struct nico1
{
    int a,b;
}t[300005];

struct nico2
{
    int num,pos;
}up[600005];
int all[600005],tp,cnt,tot[600005],ans,i,n;

bool comp(nico2 a,nico2 b)
{
    return a.num>b.num;
}

int main()
{
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);

    scanf("%d",&n);

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

        cnt++;
        all[cnt]=t[i].a;
        cnt++;
        all[cnt]=t[i].b;
    }

    sort(all+1,all+cnt+1);
    tp=unique(all+1,all+cnt+1)-all-1;

    for(i=1;i<=n;i++)
    {
        t[i].a=lower_bound(all+1,all+tp+1,t[i].a)-all;
        t[i].b=lower_bound(all+1,all+tp+1,t[i].b)-all;      
    }

    for(i=1;i<=n;i++)
    {
        tot[t[i].a]++;tot[t[i].b]++;
        if(t[i].a==t[i].b) tot[t[i].a]--;

        up[t[i].a].num++;up[t[i].a].pos=t[i].a;
    }

    sort(up+1,up+tp+1,comp);

    for(i=1;i<=tp;i++)
        if(tot[up[i].pos]>=(n+1)/2)
        {
            ans=(n+1)/2-up[i].num;
            if(ans<0) ans=0;

            printf("%d",ans);
            return 0;
        }


    printf("Impossible");
    return 0;
}

T7

利用归并排序的思想
把数换成字符串
其实我在瞎扯

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
#define MOD 1000000007
#define LL long long
using namespace std;

LL pow[50005],hash[50005];
int n,m,ans,num[50005],f[50005],tmp[50005];
char s[50005];

bool compare(int p1,int p2)
{
    int l,r,mid;
    LL hash1,hash2;

    if(p1==p2) return 1;

    l=0;r=m+1;
    if(n-p2+2<r) r=n-p2+2;
    if(n-p1+2<r) r=n-p1+2;

    while(r-l>1)
    {
        mid=(l+r)>>1;

        hash1=(hash[p1+mid-1]-hash[p1-1]*pow[mid])%MOD;
        hash2=(hash[p2+mid-1]-hash[p2-1]*pow[mid])%MOD; 
        hash1=(hash1+MOD)%MOD;hash2=(hash2+MOD)%MOD;        

        if(hash1==hash2) l=mid;
        else r=mid;
    }

    if(l==m) return 1;
    return num[p1+l]<num[p2+l];
}

void sort(int l,int r)
{
    int l1,l2,mid,cnt,i; 

    if(l==r) return;

    mid=(l+r)>>1;
    sort(l,mid);sort(mid+1,r);

    l1=l;
    l2=mid+1;
    cnt=l;
    while(l1<=mid&&l2<=r)
    {
        if(compare(f[l1],f[l2]))
        {
            tmp[cnt]=f[l1];
            cnt++;l1++;
        }   

        else
        {
            tmp[cnt]=f[l2];
            cnt++;l2++;         
            ans+=mid-l1+1;
        }
    }   

    while(l1<=mid)
    {
        tmp[cnt]=f[l1];
        cnt++;l1++;     
    }

    while(l2<=r)
    {
        tmp[cnt]=f[l2];
        cnt++;l2++;             
    }

    for(i=l;i<=r;i++) f[i]=tmp[i];      
    return;
}

int main()
{

    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);

    int i;

    scanf("%d%d",&n,&m);
    scanf("%s",s);

    pow[0]=1;
    for(i=1;i<=n;i++)
    {
        num[i]=s[i-1]-'a'+1;
        hash[i]=(hash[i-1]*29+num[i])%MOD;  
        pow[i]=(pow[i-1]*29)%MOD;   
        f[i]=i;
    }

    sort(1,n);
    printf("%d",ans);
    return 0;
}

T8

不难看出是个挺漂亮的二叉树
因为树很浅
暴力求LCA

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
#define MOD 1000000007
#define LL long long
using namespace std;

int n,pow2[30],x,y,d,dep1,dep2,dep3,rp1,rp2;

int findep(int p)
{
    int i;

    return log2(p);
}

int lca(int p1,int p2)
{   
    dep1=findep(p1);
    dep2=findep(p2);    

    rp1=dep1;rp2=dep2;

    if(dep1<dep2) 
    {
        swap(p1,p2);
        swap(dep1,dep2);    
    }

    while(dep2<dep1)
    {
        p1/=2;
        dep1--;
    }   

    while(p1!=p2)
    {
        p1/=2;
        p2/=2;
    }

    dep3=findep(p1);
    return rp1+rp2-2*dep3;
}


int main()
{
    freopen("city.in","r",stdin);
    freopen("city.out","w",stdout);


    int i;
    scanf("%d",&n);

    pow2[0]=1;
    for(i=1;i<=27;i++)
        pow2[i]=pow2[i-1]*2;

    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&x,&y);
        d=lca(x,y);
        printf("%d\n",d);
    }

    return 0;
}

T9

想当年巧克力和香子兰
还不是小黄油女主
只是两个漂亮的妹子
甚至还是不少妹子的梦想

看着n很小
所以我们来状压吧

f[i][sta]表示从家到i经过sta这个状态的最短距离
g[j][sta]表示从花店到j经过sta这个状态的最短距离
拼起来

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<climits>
#include<queue>
#include<vector>
#define MOD 1000000007
#define LL long long
using namespace std;

int i,j,k,sta,situ,n,m,dis[22][22],tot,f[20][1<<20],g[20][1<<20],x,y,z,num,minx,pos,ans,ans1,ans2;
int main()
{
    freopen("vanilla.in","r",stdin);
    freopen("vanilla.out","w",stdout);

    scanf("%d%d",&n,&m);

    memset(dis,127/3,sizeof(dis));
    for(i=0;i<=n;i++) dis[i][i]=0;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        dis[x][y]=min(dis[x][y],z);
        dis[y][x]=min(dis[y][x],z);
    }

    if(n==3)
    {
        ans=(dis[0][1]+dis[1][2])*2;
        printf("%d",ans);
        return 0;
    }

    tot=(1<<(n-2))-1;
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            for(k=0;k<n;k++)
                dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);

    memset(f,127/3,sizeof(f));
    for(i=1;i<n-1;i++) f[i][1<<(i-1)]=dis[i][0];
    for(sta=0;sta<=tot;sta++)
        for(i=1;i<n-1;i++)
            if((1<<(i-1))&sta)
                for(j=1;j<n-1;j++)
                    if(!((1<<(j-1))&sta))
                       f[j][sta|(1<<(j-1))]=min(f[j][sta|(1<<(j-1))],f[i][sta]+dis[i][j]);

    memset(g,127/3,sizeof(g));
    for(i=1;i<n-1;i++) g[i][1<<(i-1)]=dis[i][n-1];
    for(sta=0;sta<=tot;sta++)
        for(i=1;i<n-1;i++)
            if((1<<(i-1))&sta)
                for(j=1;j<n-1;j++)
                    if(!((1<<(j-1))&sta))
                       g[j][sta|(1<<(j-1))]=min(g[j][sta|(1<<(j-1))],g[i][sta]+dis[i][j]);

    ans=500000; 
    for(sta=0;sta<=tot;sta++)
    {
        situ=sta^tot;

        num=0;
        for(i=1;i<n-1;i++)
            if((1<<(i-1))&sta) num++;   
        if(num!=(n-2)/2) continue;


        ans1=500000;ans2=500000;
        for(i=1;i<n-1;i++)
            if((1<<(i-1))&sta)
                for(j=1;j<n-1;j++)
                    if((1<<(j-1))&situ)
                        ans1=min(ans1,f[i][sta]+dis[i][j]+g[j][situ]);

        for(i=1;i<n-1;i++)
            if((1<<(i-1))&sta)
                for(j=1;j<n-1;j++)
                    if((1<<(j-1))&situ)
                        ans2=min(ans2,g[i][sta]+dis[i][j]+f[j][situ]);

        ans=min(ans,ans1+ans2);
    }

    printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章