APIO2015

Decadent for a few days, every test test linked, it is now to reflect upon it!

These days always do, read, read, no feeling, it seems own spicy chicken, and even violence are frequent mistakes, appears to be written before the method is not correct, as far as possible after the beginning of the code from violence, Do positive solution is direct.

Or to say something about APIO2015 the main topic!

Since that APIO2015 watching feel good writing, but in fact as well. Each question has ideas, but little data is bundled pit. . .

1.sculpture

    这道题最开始就认为它是dp(其实也是dp),但是最开始的那个n^3的,最容易想到的dp却是错的!!!由于它的性质,所以不能直接取min!!!!!
    但是我还是完美的向这个坑跳下去了。。。。。
    (其实这样如果不WA会有71分,APIO会这么简单吗?)
    所以第一题就爆0了。T_T


    正解:
    贪心+dp
        让答案尽量小,则需要高位尽量为0.
        对于答案,我们考虑从最高位开始一次枚举,我们判断它是否可以为0,如果可以则确定下来,反之,就赋值成1.这样dp也是O(32*n^3),这样是可以过的。
        但是,有一个问题,对于这样的dp,时间复杂度在第5部分是承受不了的,然而我们又发现,对于第5部分是没有下限,我们可以分类讨论来写两个dp。
        对于第一部分,我们的dp[i][j]代表枚举到第i为,分成j段是否可以满足当前位为0,判断一下是否满足当前的状态即可。
        对于第二部分,我们的g[i]代表到第i为数,使它满足当前位为0,最少需要分多少段。

As follows:
m [] is binary template
first portion:

void solve2(){
    long long ans=0;
    Forr(l,len,0){
        ans+=m[l]-1;
        Set(dp,0);
        dp[0][0]=1;
        For(k,1,B){
            For(i,1,n){
                For(j,0,i-1){
                    dp[i][k]|=(dp[j][k-1] && ((a[i]-a[j])|ans)==ans);
                    if(dp[i][k]) break;
                }
            }
        }
        bool flag=0;
        For(i,A,B){
            if(dp[n][i]) {flag=1;break;}
        }
        if(flag) ans-=m[l]-1;
        else ++ans;
    }
    printf("%lld\n",ans);
}

the second part:

void solve1(){
    long long ans=0;
    Forr(l,len,0){
        ans+=m[l]-1;
        Set(g,63);
        g[0]=0;
        For(i,1,n){
            For(j,0,i-1){
                if((ans|(a[i]-a[j]))==ans)
                    g[i]=min(g[i],g[j]+1);
            }
        }
        if(g[n]<=B) ans-=m[l]-1;
        else ans++;
    }
    printf("%lld\n",ans);
}

In this way the first question is solved.

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define Forr(aa,bb,cc) for(int aa=bb;aa>=cc;--aa)
#define Set(aa,bb) memset(aa,bb,sizeof(aa))
using namespace std;
const int maxn=2010;
int n,A,B,len;
long long a[maxn],m[maxn],g[maxn];
bool dp[maxn][maxn];

void solve1(){
    long long ans=0;
    Forr(l,len,0){
        ans+=m[l]-1;
        Set(g,63);
        g[0]=0;
        For(i,1,n){
            For(j,0,i-1){
                if((ans|(a[i]-a[j]))==ans)
                    g[i]=min(g[i],g[j]+1);
            }
        }
        if(g[n]<=B) ans-=m[l]-1;
        else ans++;
    }
    printf("%lld\n",ans);
}

void solve2(){
    long long ans=0;
    Forr(l,len,0){
        ans+=m[l]-1;
        Set(dp,0);
        dp[0][0]=1;
        For(k,1,B){
            For(i,1,n){
                For(j,0,i-1){
                    dp[i][k]|=(dp[j][k-1] && ((a[i]-a[j])|ans)==ans);
                    if(dp[i][k]) break;
                }
            }
        }
        bool flag=0;
        For(i,A,B){
            if(dp[n][i]) {flag=1;break;}
        }
        if(flag) ans-=m[l]-1;
        else ++ans;
    }
    printf("%lld\n",ans);
}

void work(){
    scanf("%d%d%d",&n,&A,&B);
    For(i,1,n) scanf("%lld",&a[i]),a[i]+=a[i-1];
    long long tmp=1;
    len=1;
    while(tmp<a[n]){
        ++len;
        tmp<<=1;
    }
    m[0]=1;
    For(i,1,len) m[i]=m[i-1]<<1;
    if(A==1) solve1();
    else solve2();
}

int main(){
    work();
    return 0;
}

2.skyscrapers
this problem, is to look at a map topic.
And it seems relatively easy to cheat points.
36 points:
For each building doge even reach the edge, then the shortest path, MLE.
57 minutes:
to connect the building to the point edge, connected only doge buildings, most short-circuit
100:
a lot of practice, you can build auxiliary points and so on.
But I will focus on one of:

    对于这个图,我们考虑以建筑物为点,将它上面的doge的跳跃能力值都记录下来,并且去重(由于相同的再次访问浪费时间)。但是这必须限制在一定的范围内,否则会炸掉。然后最短路。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define Forr(aa,bb,cc) for(int aa=bb;aa>=cc;--aa)
#define Set(aa,bb) memset(aa,bb,sizeof(aa))
using namespace std;
const int maxn=30010,inf=0x3f3f3f3f;
int n,m;
vector<int>map[maxn];
struct doge{
    int h,w;
}dog[maxn];
int sum[maxn],dis[maxn];
bool ligature[maxn][210],vis[maxn];

queue<int>q;
int spfa(int s,int t){
    Set(dis,inf);
    q.push(s);
    dis[s]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        Forr(i,sum[u]-1,0){
            int k=map[u][i];
            for(int node=u,j=1;node-k>=0;++j){
                node-=k;
                if(dis[node]>dis[u]+j){
                    dis[node]=dis[u]+j;
                    if(!vis[node]){
                        vis[node]=1;
                        q.push(node);
                    }
                }
                if(k<=200 && ligature[node][k]) break;
            }
            for(int node=u,j=1;node+k<n;++j){
                node+=k;
                if(dis[node]>dis[u]+j){
                    dis[node]=dis[u]+j;
                    if(!vis[node]){
                        vis[node]=1;
                        q.push(node);
                    }
                }
                if(k<=200 && ligature[node][k]) break;
            }
        }
    }
    return dis[t];
}

void work(){
    scanf("%d%d",&n,&m);
    For(i,0,m-1){
        scanf("%d%d",&dog[i].h,&dog[i].w);
        map[dog[i].h].push_back(dog[i].w);
        if(dog[i].w<=200) ligature[dog[i].h][dog[i].w]=1;
    }
    For(i,0,n-1){
        sort(map[i].begin(),map[i].end());
        sum[i]=unique(map[i].begin(),map[i].end())-map[i].begin();
    }
    int ans=spfa(dog[0].h,dog[1].h);
    printf("%d\n",ans>=inf?-1:ans);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("skyscraper.in","r",stdin);
    freopen("skyscraper.out","w",stdout);
#endif
    work();
    return 0;
}

3.Bridges
this question there is a mathematical nature, that is, the optimal solution must be in the median (to think).
And we found that, for the A side or B side, we can see it as the same side of the bridge's cost is the same, and finally unified accumulate. At the same time, the bridge must be in the home or office that coordinates (to think)
22 points:

    k=1,暴力找中位数,

31 points:

    由于满足上述性质,所以枚举桥的位置,k=2可以过小数据。

A little bit more:

    由于是距离中位数的距离和,所以我们可以用中点来进行排序,然后枚举来做。

100 points:

    我们发现在中点排序后,插入后面的是不会影响前面的答案,所以我们可以利用数据结构来进行维护中位数。权值线段树便可以发挥作用了。
    权值线段树是可以支持整个区间上的第k大,所以求中位数也是可以的。(最主要比主席树好写多了)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define Set(aa,bb) memset(aa,bb,sizeof(aa))
#define rank Rank
#define ls node<<1
#define rs node<<1|1
using namespace std;
typedef long long LL;
const int maxn=300010;
LL ans[maxn];
struct asdf{
    LL sum;
    int size;
}tree[maxn<<1];
struct asdfg{
    int x,y;
}a[maxn<<1];
int n,m,tot,size,rank[maxn];

int ask(int node){
    return lower_bound(rank+1,rank+1+size,node)-rank;
}

bool cmp(asdfg c,asdfg b){
    return c.x+c.y<b.x+b.y;
}

void pushup(int node){
    tree[node].sum=tree[ls].sum+tree[rs].sum;
    tree[node].size=tree[ls].size+tree[rs].size;
}

void create_tree(int node,int l,int r){
    if(l==r){
        tree[node].sum=tree[node].size=0;
        return ;
    }
    int mid=(l+r)>>1;
    create_tree(ls,l,mid);
    create_tree(rs,mid+1,r);
    pushup(node);
    return ;
}

void insert(int node,int l,int r,int k){
    if(l==r){
        tree[node].sum+=rank[l];
        ++tree[node].size;
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid) insert(ls,l,mid,k);
    else insert(rs,mid+1,r,k);
    pushup(node);
    return ;
}

LL query(int node,int l,int r,int k){
    if(tree[node].size<=k) return tree[node].sum;
    if(l==r) return 1LL*k*rank[l];
    int mid=(l+r)>>1;
    if(tree[ls].size>=k) return query(ls,l,mid,k);
    else return tree[ls].sum+query(rs,mid+1,r,k-tree[ls].size);
}

LL Query(int k){
    return tree[1].sum-2LL*query(1,1,size,k);
}

void work(){
    LL hh=0;
    scanf("%d%d",&m,&n);
    For(i,1,n){
        char s1[2],s2[2];int x,y;
        scanf("%s%d%s%d",s1,&x,s2,&y);
        if(s1[0]==s2[0]){
            hh+=abs(x-y);
            continue;
        }
        ++hh;
        rank[++size]=x,rank[++size]=y;
        a[++tot].x=x,a[tot].y=y;
    }
    sort(rank+1,rank+1+size);
    size=unique(rank+1,rank+1+size)-rank-1;
    if(size){
        create_tree(1,1,size);
        sort(a+1,a+1+tot,cmp);
        For(i,1,tot){
            insert(1,1,size,ask(a[i].x));
            insert(1,1,size,ask(a[i].y));
            ans[i]=Query(i);
        }
    }
    if(m==1) printf("%lld\n",ans[tot]+hh);
    else{
        LL Ans=ans[tot];
        if(size){
            create_tree(1,1,size);
            for(int i=tot;i>1;--i){
                insert(1,1,size,ask(a[i].x));
                insert(1,1,size,ask(a[i].y));
                Ans=min(ans[i-1]+Query(tot-i+1),Ans);
            }
        }
        printf("%lld\n",Ans+hh);
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("bridge.in","r",stdin);
    freopen("bridge.out","w",stdout);
#endif
    work();
    return 0;
}
Published 51 original articles · won praise 6 · views 20000 +

Guess you like

Origin blog.csdn.net/qq_35776579/article/details/55522527