[APIO2015]题解

说实话今年的APIO不是太难

但仍然阻止不了我酱油

26分TAT

巴厘岛的雕塑、巴邻旁之桥暴力

摩天楼那题SPFA莫名写跪了

第一题知道是动规不会写方程TAT

膜拜cstdio的位运算

第一题 巴厘岛的雕塑

题目大意是N个数,分为[A,B]个组

使得每组求和后异或和最小

话说为什么是最小TAT,强行黑一波印尼政府

9分算法

每两个之间判断放不放隔板,时间复杂度O(2^n)

71分算法

考虑贪心

从答案(二进制)的最高位到最低位,逐位判断是否能为0

定义状态f[i][j]

表示前i个数分成j位满足前答案前k-1位的是否可行,且k=1

然后枚举区间[A,B],判断是否对于A<=i<=B,存在f[n][i]=true,如果存在,则该位可以为0

时间复杂度O(log2M*N^3)

M为答案十进制长度

100分算法

我们注意到最后一组数据A=1,也就是没有下限,于是我们用一维数组F[i]

代替f[i][j],

F[i]表示前i个数最少要分为几组,最后判断是否F[n]<=B即可

    #include <fstream>
    //#include <iostream>
    #include <cstring>
    #define LL long long
    using namespace std;
    ifstream cin("sculpture.in");
    ofstream cout("sculpture.out");
    int n,a,b;
    int A[2001]={0};
    bool f[2001][2001]={0};
    long long S[2001]={0};
    long long limit;
    int F[2001]={0};
    long long bark(int x)
    {
        long long ans=1;
        ans=ans<<x;
        //cout<<ans<<endl;
        return ans;
    }
    bool mark()
    {
        int i,j,k;
        memset(f,0,sizeof(f));
        f[0][0]=1;
        for(i=0;i<n;i++)
        {
            for(j=0;j<b;j++)
            {
                if(f[i][j])
                {
                    for(k=i+1;k<=n;k++)
                    {
                        if(((S[k]-S[i])|limit)==limit)
                        {
                            f[k][j+1]=1;
                        }
                    }
                }
            }
        }
        //cout<<a<<‘ ‘<<b<<endl;
        for(k=a;k<=b;k++)if(f[n][k])return 1;
        return 0;
    }
    bool dark()
    {
        int i,j,k;
        memset(F,63,sizeof(F));
        F[0]=0;
        for(i=0;i<n;i++)
        {
            for(k=i+1;k<=n;k++)
            {
                if(((S[k]-S[i])|limit)==limit)
                {
                    F[k]=min(F[k],F[i]+1);
                }
            }
        }
        return F[n]<=b;
    }
    void judge(int x)
    {
        bool OK=0;
        limit-=bark(x);
        if(a!=1)OK=mark();
        if(a==1)OK=dark();
        //cout<<OK<<endl;
        if(!OK)limit+=bark(x);
    }
    int main()
    {
        int i,j,k;
        cin>>n>>a>>b;
        for(i=1;i<=n;i++)cin>>A[i];
        for(i=1;i<=n;i++)S[i]=S[i-1]+A[i];
        //for(i=1;i<=n;i++)cout<<S[i]<<‘ ‘;
        limit=bark(46)-1;
        //cout<<limit<<endl;
        for(i=45;i>=0;i--)judge(i);
        cout<<limit<<endl;
        return 0;
    }

第二题 雅加达的摩天楼

题目大意:

有N个建筑物,M只狗

告诉每只狗的初始建筑物位置b

和跳跃能力p

每只狗一步可以调到b+p或b-p的建筑物上

求0号狗到1号狗最少需要的跳跃步数

很容易看出这题是一个最短路径问题

关键在建边

10分算法

强行一波DFS(尼玛比正解还难打估计没人打)

22分算法

把每只狗能到达的所有位置建边,边的数量会爆

57分算法

稍微加一个优化即可,只与有狗的建筑物建边

100分算法(伪)

由于题目数据略水,不需要加什么特别强的优化

去掉同一个建筑物上跳跃能力相同的狗,对某些数据可以大大加快速度

97分算法(UOJ)

/*以下内容本人并不会,摘自http://blog.csdn.net/regina8023/article/details/45766241,感谢作者

考虑分块

对于跳跃能力大于sqrt(n)的狗,我们直接建边

对于跳跃能力小于sqrt(n)的狗,

我们可以在后面添加一些辅助点来实现预处理:
枚举这sqrt(n)个长度,在后面分别建n个辅助点,连向前面对应的位置,同时连向可以来自和到达的辅助点。

对于读入直接连向他对应的辅助点即可。*/

对于最短路径的求法,我推荐Dij,因为本题十分特殊,边的数量远远大于点的数量,用SPFA较快

可是我不知道为什么我的加优先队列的DIJ会挂一组

最后还是用了SPFA,注:我下面的程序没有分块

#include <fstream>
#include <queue>
#include <vector>
#include <algorithm>
#include <string.h>
#include <map>
using namespace std;
ifstream cin("skyscraper.in");
ofstream cout("skyscraper.out");
int n,m;
bool l[30001]={0};
vector<int> D[30001],G[30001],C[30001],A,Q,E[30001];
int len[30001]={0};
int f[30001]={0};
bool visit[30001]={0};
int INF=9999999;
class node
{
public:
    int x;
    int y;
}ab[30001];
map<node,bool>F;
void SPFA(int s) { // SPFA
    queue<int> q;
    l[s] = 1;
    fill_n(f, n, INF); f[s] = 0;
    q.push(s);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        l[u] = 0;
        for(int i=0; i<G[u].size(); i++) {
            int v = G[u][i];
            if(f[v] > f[u] + C[u][i]) {
                f[v] = f[u] + C[u][i];
                if(!l[v]) {
                    l[v] = 1;
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    int i,j,k,a,b,start,end;
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
        cin>>ab[i].x>>ab[i].y;
        visit[ab[i].x]=1;
    }
    for(i=1;i<=m;i++)
    {
        a=ab[i].x;
        b=ab[i].y;
        int c=ab[i-1].x;
        int d=ab[i-1].y;
        if(a==c&&b==d&&i>=10)continue;
        for(j=a+b;j<=n;j+=b)
        {
            //cout<<j<<‘ ‘<<endl;
            if(!visit[j])continue;
            G[a].push_back(j);
            C[a].push_back((j-a)/b);
        }
        for(j=a-b;j>=0;j-=b)
        {
            if(!visit[j])continue;
            G[a].push_back(j);
            C[a].push_back((a-j)/b);
        }
        if(i==1)start=a;
        if(i==2)end=a;
    }
    //memset(l,sizeof(l),0);
    SPFA(start);
    if(start==end)cout<<0<<endl;
    else if(f[end]!=INF)cout<<f[end]<<endl;
    else cout<<-1<<endl;
    return 0;
}

第三题:巴邻旁之桥

题目大意比较麻烦,这里就不说了

家和办公室的同一侧的,预处理提前算好

我们可以通过暴力程序和或手算发现,

当k=1时,我们目标是最小化|a-x|+|b-x|

不难发现答案是所有家和办公室的中位数(考场上只顾着打暴力啦)

/*以下内容本人还没有掌握,摘自http://www.bubuko.com/infodetail-809274.html

对于k=2时

按照每个人的家和办公室的中点排序后,一定存在一个分割点使得前缀都走左边的桥,后缀都走右边的桥(因为走靠近中点的桥不会更差)。

于是我们枚举分割点,离散化后用权值线段树动态维护两个区间的中位数求解即可。*/然而我并不会写线段树

目前我只拿到了31分

31分算法

//May 20 2015
//By Satoshi
//#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <map>
#define LL long long
using namespace std;
ifstream cin("cstdiorank1AK.in");
ofstream cout("cstdiorank1AK.out");
int aa[100001]={0},bb[100001]={0};
int cc[200001]={0};
LL ans=0;
int n,K;
map<int,bool>F;
vector<int> q;
void work1()
{
    int pos,i;
    pos=cc[n];
    //out<<pos<<endl;
    for(i=1;i<=n;i++)
    {
        //out<<ans<<endl;
        ans+=abs(pos-bb[i])+abs(pos-aa[i])+1;
    }
}
void work2()
{
    int i,j,k;
    LL best=1LL<<51;
    LL sum=0;
    LL u,v,w;
    for(i=0;i<q.size()-1;i++)
    {
        for(j=i+1;j<q.size();j++)
        {
            sum=0;
            for(k=1;k<=n;k++)
            {
                u=abs(aa[k]-q[i])+abs(bb[k]-q[i]);
                v=abs(aa[k]-q[j])+abs(bb[k]-q[j]);
                w=min(u,v)+1;
                sum+=w;
            }
            if(sum<best)best=sum;
        }
    }
    ans+=best;
}
int main()
{
    char a,c;
    int i,j;
    int b,d;
    int size=0;
    cin>>K>>n;
    for(i=1;i<=n;i++)
    {
        cin>>a>>b>>c>>d;
        if(a==c)ans+=abs(b-d);
        else
        {
            size++;
            aa[size]=b;
            bb[size]=d;
        }
        if(F[b]==0)
        {
            q.push_back(b);
            F[b]=1;
        }
        if(F[d]==0)
        {
            q.push_back(d);
            F[d]=1;
        }
    }
    n=size;
    //out<<ans<<endl;
    for(i=1;i<=n;i++)
    {
        cc[i]=aa[i];
        cc[n+i]=bb[i];
    }
    sort(cc+1,cc+2*n+1);
    //for(i=1;i<=n*2;i++)out<<cc[i]<<‘ ‘;
    if(K==1)work1();
    if(K==2)work2();
    //for(i=0;i<q.size();i++)out<<q[i]<<‘ ‘;

    cout<<ans<<endl;
    return 0;
}

如有错误请指出,谢谢(^_^)

时间: 2024-10-05 20:58:20

[APIO2015]题解的相关文章

APIO2015题解

分组赛讲课讲了APIO2015的题,于是回去就做完了 稍微写一点题解吧 bzoj4069 逐位处理的简单题,然后就是bool型dp 然后a=1 的时候可以把一位状态干掉 当一维状态单调且是bool型dp时,我们可以用dp表示这一维状态:类似的思想也在bzoj1937出现过 1 var s:array[0..2010] of int64; 2 n,a,b,i,j,k,p:longint; 3 g,c:array[0..2010] of longint; 4 f:array[0..110,0..11

【题解】P3645 [APIO2015]雅加达的摩天楼(分层图最短路)

[题解]P3645 [APIO2015]雅加达的摩天楼(分层图最短路) 感觉分层图是个很灵活的东西 直接连边的话,边数是$O(n^2)$的过不去 然而我们有一个优化的办法,可以建一个新图$G=(V,E)$其中$V$和原图$V$一一对应且连接一个$0$边,此外每个点向V中的$i+-d$连边. 类似网络流的办法瞎建就行了. 过不了uoj //@winlere #include<iostream> #include<cstdio> #include<cstring> #inc

APIO2015简要题解

最近老师把apio的题目拿出来了,然后由于我实在是菜,分数还没三位数...... ----------------我是分割线 1.巴厘岛的雕塑 N个数,分成连续的A-B个组,让每个组的和或起来最小,求最小值. 对于Task1 n<=100 由于涉及到位运算,所以很容易想到按二进制位来做.要让答案最小,显然要从二进制高位到低位判断,能取0就取0. 所以我们考虑一个n^3 dp  用 f[i][j] 表示在当前的值之下,前i个分j组能否符合条件,用x表示当前判断的数字. f[i][j]|=f[k][

BZOJ4071 &amp; 洛谷3644:[APIO2015]巴邻旁之桥——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4071 https://www.luogu.org/problemnew/show/P3644 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 1000000000.相邻的每对建筑相隔 1 个单位距离,河的宽度也是 1 个单位长度.区域 A 中的 i 号建筑物恰好与区域 B 中的

【BZOJ4069】[Apio2015]巴厘岛的雕塑 按位贪心+DP

[BZOJ4069][Apio2015]巴厘岛的雕塑 Description 印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道. 在这条主干道上一共有 N 座雕塑,为方便起见,我们把这些雕塑从 1 到 N 连续地进行标号,其中第 i 座雕塑的年龄是 Yi 年.为了使这条路的环境更加优美,政府想把这些雕塑分成若干组,并通过在组与组之间种上一些树,来吸引更多的游客来巴厘岛. 下面是将雕塑分组的规则: 这些雕塑必须被分为恰好 X 组,其中 A< = X< = B,每组必须含有至少一个雕塑,每

贪心(qwq)习题题解

贪心(qwq)习题题解 SCOI 题解 [ SCOI2016 美味 ] 假设已经确定了前i位,那么答案ans一定属于一个区间. 从高位往低位贪心,每次区间查找是否存在使此位答案为1的值. 比如6位数确定了前三位\((101...)_2\),下一位应该是1 那么\(a_i+x_i\)的查找区间为:\([(101100)_2,(101111)_2]\) ,同理如果应该是0则为\([(101000)_2,(101011)_2]\). 注意到有区间\([l,r]\)范围的限制所以用主席树维护即可. [

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

8.8联考题解

今天的T1让我怀疑我是不是在做奥赛题--这考的是什么知识点啊这个,会不会用绝对值函数? Evensgn 的债务 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Evensgn 有一群好朋友,他们经常互相借钱.假如说有三个好朋友A,B,C.A 欠 B 20 元,B 欠 C 20 元,总债务规模为 20+20=40 元.Evensgn 是个追求简约的人,他觉得这样的债务太繁杂了.他认为,上面的债务可以完全等价为 A 欠C20 元,B 既不欠别人,别人也不欠他.这样总债务规模就压缩到了 

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)