标签:
说实话今年的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; }
如有错误请指出,谢谢(^_^)
标签:
原文地址:http://www.cnblogs.com/Satoshi/p/4524805.html