APIO2015 UOJ #110 - #112 题解
2016-04-27 20:14
281 查看
T1:给你一个序列A,问将A划分成K块(A <= K <= B),将每块中的元素求和,再按位或能得到的最小值是多少(话说为毛是最小,这背后怕是有奇怪的交易)
有个显然的想法是按二进制位依次考虑。那么假设当前在考虑第pos位,之前位的ans已经确定,我们可以用dp[[i][j]表示前i个元素分成j块能否满足条件,那么枚举断点,再判断一下就行。最后看这一位能否为0,就是找dp
[i] (A <= i <= B)中有没有为true的项。
上述做法复杂度为O(N ^ 3 * log(Ans)),无法通过最后一个子任务。
但我们注意到最后一个子任务中A等于1,即没有下限,那么我们可以用dp[i]表示前i个满足条件最少要分多少块,最后判断dp
是否 <= B即可,复杂度O(N ^ 2 * log(Ans)).
代码如下:
T2:给你N个点M个边集,每个边集是从一个点开始,每隔相同的格子就连边。求两点间最短路。
一眼看上去就是要分块咯。若边集小于sqrt(N)则直接连边,否则的话:对于每个点为起点,只有sqrt(N)种连边方式,那我们新建N * sqrt(N)个点,每层之间互相连边。对于每个边集就直接往对应的起点的对应辅助点连边即可。
这样点数和边数均为O(N * sqrt(N)),然后跑最短路即可。
HINT:我写了个spfa只拿了97分,估计是被卡了,不过感觉dijkstra好不到哪里去。注意到边权都为0或1,那么可以直接用bfs代替最短路,如果通过边权为0的边更新了距离,那么将点加入队首,否则加入队尾。(不过懒得写了,嘴巴选手的感觉真爽)
97分代码如下:
T3:有一条河,可以建造K座桥(K <= 2),桥必须垂直于河,河宽度为1,有N个人,给出他们家和办公地点的位置,问最短总距离。
对于K=1的情况:桥显然要建在所有坐标的中位数(不用过河的人先去掉);
对于K=2的情况:一个人肯定要走离他两个坐标平均值距离最近的那座桥,那么把所有人按两个坐标平均值排序,对于任意建造方案,定有一个分割点,使得分割点左边的点和右边的点走不同的桥,那么枚举分割点,转化成K=1的情况即可。
代码如下:
有个显然的想法是按二进制位依次考虑。那么假设当前在考虑第pos位,之前位的ans已经确定,我们可以用dp[[i][j]表示前i个元素分成j块能否满足条件,那么枚举断点,再判断一下就行。最后看这一位能否为0,就是找dp
[i] (A <= i <= B)中有没有为true的项。
上述做法复杂度为O(N ^ 3 * log(Ans)),无法通过最后一个子任务。
但我们注意到最后一个子任务中A等于1,即没有下限,那么我们可以用dp[i]表示前i个满足条件最少要分多少块,最后判断dp
是否 <= B即可,复杂度O(N ^ 2 * log(Ans)).
代码如下:
/* * @Author: 逸闲 * @Date: 2016-04-25 11:33:23 * @Last Modified by: 逸闲 * @Last Modified time: 2016-04-25 12:09:05 */ #include "cstdio" #include "cstdlib" #include "iostream" #include "algorithm" #include "cstring" #include "queue" using namespace std; #define INF 0x3F3F3F3F #define Eps #define Mod #define Get(x, a) (x ? x -> a : 0) inline int Get_Int() { int Num = 0, Flag = 1; char ch; do { ch = getchar(); if(ch == '-') Flag = -Flag; } while(ch < '0' || ch > '9'); do { Num = Num * 10 + ch - '0'; ch = getchar(); } while(ch >= '0' && ch <= '9'); return Num * Flag; } int N, A, B; namespace Task_1_4 { const int MAX_SIZE = 105; long long Ans; long long Sum[MAX_SIZE]; bool DP[MAX_SIZE][MAX_SIZE]; inline void Solve() { for(int i = 1; i <= N; ++i) Sum[i] = Sum[i - 1] + Get_Int(); long long Now = 0; while(1LL << Now < Sum ) ++Now; for(; Now >= 0; --Now) { memset(DP, false, sizeof(DP)); DP[0][0] = true; for(int i = 1; i <= N; ++i) for(int j = 1; j <= B; ++j) for(int k = 0; k < i; ++k) if(DP[k][j - 1]) { long long temp = Sum[i] - Sum[k]; if((((temp >> Now << Now) | Ans) == Ans) && ((temp & (1LL << Now)) == 0)) DP[i][j] = true; } bool Flag = false; for(int i = A; i <= B; ++i) if(DP [i]) { Flag = true; break; } if(!Flag) Ans += 1LL << Now; } cout << Ans << endl; } } namespace Task_5 { const int MAX_SIZE = 2005; long long Ans; long long Sum[MAX_SIZE]; int DP[MAX_SIZE]; inline void Solve() { for(int i = 1; i <= N; ++i) Sum[i] = Sum[i - 1] + Get_Int(); long long Now = 0; while(1LL << Now < Sum ) ++Now; for(; Now >= 0; --Now) { memset(DP, 0x3F, sizeof(DP)); DP[0] = 0; for(int i = 1; i <= N; ++i) for(int j = 0; j < i; ++j) { long long temp = Sum[i] - Sum[j]; if((((temp >> Now << Now) | Ans) == Ans) && ((temp & (1LL << Now)) == 0)) DP[i] = min(DP[i], DP[j] + 1); } if(DP > B) Ans += 1LL << Now; } cout << Ans << endl; } } int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif cin >> N >> A >> B; if(N <= 100) Task_1_4::Solve(); else Task_5::Solve(); fclose(stdin); fclose(stdout); return 0; }
T2:给你N个点M个边集,每个边集是从一个点开始,每隔相同的格子就连边。求两点间最短路。
一眼看上去就是要分块咯。若边集小于sqrt(N)则直接连边,否则的话:对于每个点为起点,只有sqrt(N)种连边方式,那我们新建N * sqrt(N)个点,每层之间互相连边。对于每个边集就直接往对应的起点的对应辅助点连边即可。
这样点数和边数均为O(N * sqrt(N)),然后跑最短路即可。
HINT:我写了个spfa只拿了97分,估计是被卡了,不过感觉dijkstra好不到哪里去。注意到边权都为0或1,那么可以直接用bfs代替最短路,如果通过边权为0的边更新了距离,那么将点加入队首,否则加入队尾。(不过懒得写了,嘴巴选手的感觉真爽)
97分代码如下:
* * @Author: duyixian * @Date: 2016-04-25 20:00:18 * @Last Modified by: duyixian * @Last Modified time: 2016-04-25 20:27:05 */ #include "cstdio" #include "cstdlib" #include "iostream" #include "algorithm" #include "cstring" #include "queue" #include "cmath" using namespace std; #define INF 0x3F3F3F3F #define MAX_SIZE 30005 #define Eps #define Mod #define Get(x, a) (x ? x -> a : 0) #define Travel(x) for(typeof(x.begin()) it = x.begin(); it != x.end(); ++it) inline int Get_Int() { int Num = 0, Flag = 1; char ch; do { ch = getchar(); if(ch == '-') Flag = -Flag; } while(ch < '0' || ch > '9'); do { Num = Num * 10 + ch - '0'; ch = getchar(); } while(ch >= '0' && ch <= '9'); return Num * Flag; } class Edge { public: int To, Next, C; }Edges[MAX_SIZE * 102 * 8]; int N, M, Total, S, T, Size = 100; int Front[MAX_SIZE * 102], Distance[MAX_SIZE * 102], B[MAX_SIZE], P[MAX_SIZE]; bool InQueue[MAX_SIZE * 102]; inline void Add_Edge(int From, int To, int C) { //printf("%d %d %d\n", From, To, C); Edges[++Total].To = To; Edges[Total].Next = Front[From]; Edges[Total].C = C; Front[From] = Total; } inline void Add_Edges(int From, int To, int C) { Add_Edge(From, To, C); Add_Edge(To, From, C); } inline void SPFA() { memset(Distance, 0x3F, sizeof(Distance)); Distance[S] = 0; queue<int> Queue; Queue.push(S); InQueue[S] = true; while(!Queue.empty()) { int Now = Queue.front(); Queue.pop(); InQueue[Now] = false; for(int i = Front[Now]; i; i = Edges[i].Next) if(Distance[Edges[i].To] > Distance[Now] + Edges[i].C) { Distance[Edges[i].To] = Distance[Now] + Edges[i].C; if(!InQueue[Edges[i].To]) { Queue.push(Edges[i].To); InQueue[Edges[i].To] = true; } } } } int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif cin >> N >> M; Size = min(Size, (int)sqrt(N)); for(int i = 1; i <= M; ++i) { B[i] = Get_Int() + 1; P[i] = Get_Int(); } S = B[1]; T = B[2]; for(int i = 1; i <= N; ++i) for(int j = 1; j <= Size; ++j) { Add_Edge(i + j * N, i, 0); if(i + j <= N) Add_Edges(i + j * N, i + j + j * N, 1); } for(int i = 1; i <= M; ++i) { int Now = B[i]; if(P[i] > Size) { for(int j = 1; Now + j * P[i] <= N; ++j) Add_Edge(Now, Now + j * P[i], j); for(int j = 1; Now - j * P[i] >= 1; ++j) Add_Edge(Now, Now - j * P[i], j); } else Add_Edge(Now, Now + P[i] * N, 0); } SPFA(); if(Distance[T] != INF) cout << Distance[T] << endl; else cout << -1 << endl; fclose(stdin); fclose(stdout); return 0; }
T3:有一条河,可以建造K座桥(K <= 2),桥必须垂直于河,河宽度为1,有N个人,给出他们家和办公地点的位置,问最短总距离。
对于K=1的情况:桥显然要建在所有坐标的中位数(不用过河的人先去掉);
对于K=2的情况:一个人肯定要走离他两个坐标平均值距离最近的那座桥,那么把所有人按两个坐标平均值排序,对于任意建造方案,定有一个分割点,使得分割点左边的点和右边的点走不同的桥,那么枚举分割点,转化成K=1的情况即可。
代码如下:
/* * @Author: 逸闲 * @Date: 2016-04-26 10:18:50 * @Last Modified by: 逸闲 * @Last Modified time: 2016-04-26 13:50:14 */ #include "cstdio" #include "cstdlib" #include "iostream" #include "algorithm" #include "cstring" #include "queue" using namespace std; #define INF 0x3F3F3F3F #define MAX_SIZE 200005 #define Eps #define Mod #define Get(x, a) (x ? x -> a : 0) #define L(i) (i ? Mid + 1 : Left) #define R(i) (i ? Right : Mid) inline int Get_Int() { int Num = 0, Flag = 1; char ch; do { ch = getchar(); if(ch == '-') Flag = -Flag; } while(ch < '0' || ch > '9'); do { Num = Num * 10 + ch - '0'; ch = getchar(); } while(ch >= '0' && ch <= '9'); return Num * Flag; } namespace Segment_Tree { class Node { public: Node *Child[2]; long long Sum, Size; }Nodes[MAX_SIZE * 35]; Node *Total = Nodes; Node* Mofidy(Node *x, int Position, int Value, int Left, int Right) { if(!x) x = Total++; x -> Size += Value; x -> Sum += (long long)Value * Position; if(Left != Right) { int Mid = Left + Right >> 1, i = Position > Mid; x -> Child[i] = Mofidy(x -> Child[i], Position, Value, L(i), R(i)); } return x; } long long Query(Node *x, int K, long long &temp, int Left, int Right) { if(!x) return 0; long long Ans = 0; if(Left == Right) { temp = Left; return 0; } int LeftSize = Get(x -> Child[0], Size); int i = 0, Mid = Left + Right >> 1; if(LeftSize < K) K -= LeftSize, i = 1; Ans += Query(x -> Child[i], K, temp, L(i), R(i)); if(i) Ans += temp * Get(x -> Child[0], Size) - Get(x -> Child[0], Sum); else Ans += Get(x -> Child[1], Sum) - temp * Get(x -> Child[1], Size); return Ans; } } class Point { public: int Left, Right; inline bool operator < (Point const &a) const { return Left + Right < a.Left + a.Right; } }Points[MAX_SIZE]; int K, N, Total; long long Ans, Min; Segment_Tree::Node *Root1, *Root2; int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif cin >> K >> N; for(int i = 1; i <= N; ++i) { char temp1[3], temp2[3]; int Left, Right; scanf("%s%d%s%d", temp1, &Left, temp2, &Right); if(Left > Right) swap(Left, Right); if(temp1[0] == temp2[0]) Ans += (long long)(Right - Left); else Points[++Total] = (Point){Left, Right}; } Ans += (long long)Total; sort(Points + 1, Points + Total + 1); long long temp; for(int i = 1; i <= Total; ++i) { Root1 = Segment_Tree::Mofidy(Root1, Points[i].Left, 1, 0, INF); Root1 = Segment_Tree::Mofidy(Root1, Points[i].Right, 1, 0, INF); } if(K == 1) cout << Ans + Segment_Tree::Query(Root1, Get(Root1, Size) >> 1LL, temp, 0, INF) << endl; else { Min = Segment_Tree::Query(Root1, Get(Root1, Size) >> 1, temp, 0, INF); for(int i = Total; i; --i) { Root1 = Segment_Tree::Mofidy(Root1, Points[i].Left, -1, 0, INF); Root1 = Segment_Tree::Mofidy(Root1, Points[i].Right, -1, 0, INF); Root2 = Segment_Tree::Mofidy(Root2, Points[i].Left, 1, 0, INF); Root2 = Segment_Tree::Mofidy(Root2, Points[i].Right, 1, 0, INF); Min = min(Min, Segment_Tree::Query(Root2, Get(Root2, Size) >> 1LL, temp, 0, INF) + Segment_Tree::Query(Root1, Get(Root1, Size) >> 1LL, temp, 0, INF)); } cout << Ans + Min << endl; } fclose(stdin); fclose(stdout); return 0; }
相关文章推荐
- HDU 1323 Perfection(公因子)
- Maven私服问题汇总---学习笔记
- 集合的三种遍历方式
- Heap Data Structure and Heap Sort
- Scrum 之 每日站会
- 如何修改已经提交SVN的log
- Java资源文件读取
- poj3468——A Simple Problem with Integers(线段树,区间更新)
- EventBus使用详解(二)——EventBus使用进阶
- Android应用启动界面分析(Starting Window)
- 【AR】关于AR实时阴影的制作
- cocos2dx 3.10 黄金矿工学习笔记
- nyoj 20
- Java基础 | 程序入口main()方法
- 你足够了解Context吗?
- Java IO流的使用
- iOS UI调试神器,插件injection for Xcode使用方法
- 调用Camera进行照相并将照片返回到App界面
- redis持久化
- EventBus使用详解(一)——初步使用EventBus