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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章