[APIO2015]题解

it2022-05-05  168

说实话今年的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; }

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

转载于:https://www.cnblogs.com/Satoshi/p/4524805.html

相关资源:各显卡算力对照表!

最新回复(0)