[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; }
如有错误请指出,谢谢(^_^)