/**//* N行,有M个僵尸,有K个坚果,按顺序从给定线路水平滚出 坚果撞到僵尸或者边界都会改变路线以45°滚动,问最后多少只僵尸被撞到了 N<=2000, M<=200000, K<=100000 我之前是按顺序模拟坚果滚动,超时了 虽然我也是建了list之类的,我知道的是能用x+y,x-y的值来表示直线 复杂度应该是O(MlogM + K)
watashi解题报告说可以用链表类似Dancing Links啥的搞到O(M+K),但是编程量巨大 他自己的做法很神奇!!!! 反过来做,对僵尸按X排序,从左到右,检查该僵尸会被哪只坚果撞到(其实就是被编号最小的撞到) 一只僵尸(x,y)可以被3个方向撞到(y=1,n时两个),路线的方程就是 x - y = k1 , x + y = k2 , y = k3 因此需要找到满足上面方程之一的坚果,而且编号最小的 直接这样搞,方程很多的,我之前就是这样子搞..
注意到x-y = k1与x-2(n-1)-y=k1是同一条路线的,所以可以对(x-y)%2(n-1),x+y也一样 取模了后本来x-y很大的,就变为很小了,而且是可唯一标示出来的 两个点A,B若在同一条路线上,若他们都向下或向上的话,显然 y1-x1 = y2-x2 mod m , m-(y1+x1) = m-(y2+x2) mod m 若一上一下,就 y1-x1 = m-(y2+x2) mod m 这样一条路线就能用一个值来表示了,即在点(x,y)处, ---------------------------------------------- |若向下则该路线的值(y-x) mod m | |若向上则该路线的值m-(y+x) mod m | ---------------------------------------------- 为什么能这样表示呢? 比如向下的直线方程为y-x = k1,向上的为y+x=k2 他们关于边界对称,则有k1+k2 = m,即k1 = m - k2,所以用K1,m-k2就能唯一表示线路了(曲折的,不是直线) \ / \/ ------- m/2 下面的那个是引出的,上面的是实际路线,显然这两条直线跟y轴的交点之和k1+k2为m / /
在起始处总共也就只会有n-1条向下的,n-1条向上的,n条水平的,这2*(n-1)+n条直线就会铺满整个地图了 因此可编码为 [0,n-1)的为向下的,[n-1,2(n-1))向上的,[2*(n-1),3*n-2)水平的
启示:处理好同一条路线怎么用一个值来表示
枚举点(x,y)可能被几个方向撞到的线路,这些路线就是m+y, (y-x) mod m , m-(y+x) mod m 最后就用优先队列来搞,取编号最小的路线,因为最先被这个撞到了~~~ 处理好一列后,注意加入新的坚果路线(因为撞了后改变路线了)
神奇丫神奇丫~~~~
以上只是我膜拜了watashi的代码后的一点想法而已,建议直接看他的代码~~~ */ #include<iostream> #include<cstring> #include<map> #include<algorithm> #include<stack> #include<queue> #include<cmath> #include<string> #include<cstdlib> #include<vector> #include<cstdio> #include<set> #include<list> #include<numeric> #include<cassert> #include<ctime> #include<bitset>
using namespace std;
const int MAXN =200000;
pair<int,int> pt[MAXN]; priority_queue<int, vector<int>, greater<int> > pq[6000];//最小堆
inline int mod(int a, int b) { a %= b; return a < 0 ? a + b : a; }
int main() { #ifndef ONLINE_JUDGE freopen("in","r",stdin); #endif
int T, N, M, K; for(scanf("%d", &T); T--; ) { scanf("%d%d%d", &N, &M, &K); int m = 2*(N-1);
for(int k = 0; k < m + N ; k++) { while(!pq[k].empty()){ pq[k].pop(); } }
for (int i = 0; i < M; i ++) { scanf("%d%d", &pt[i].first, &pt[i].second); pt[i].second --; } sort(pt, pt+M);
for (int i = 0, t; i < K; i ++) { scanf("%d", &t); pq[m+t-1].push(i); } int lastx = 0, ans = 0; vector<pair<int,int> > r; for(int i = 0; i < M; i ++) { int x = pt[i].first , y = pt[i].second; if (x > lastx) {//处理完一列后注意加入新的坚果路线 lastx = x; for (vector<pair<int,int> >::iterator it = r.begin() ; it != r.end(); it ++) { pq[it->first].push(it->second); } r.clear(); }
int j = -1, t; if(!pq[t = m+y].empty() && (j == -1 || pq[t].top() < pq[j].top())){// horizonal j = t; } if(!pq[t = mod(y-x, m)].empty() && ( j == -1 || pq[t].top() < pq[j].top()) ){ //buttom right j = t; } if(!pq[t = mod(m-(y+x), m)].empty() && ( j == -1 || pq[t].top() < pq[j].top()) ){ //top right j = t; } if(j == -1){ continue; }
ans ++; t = pq[j].top(); pq[j].pop(); if(j < m){ r.push_back(make_pair(mod(m-j-2*x, m),t)); }else { j -= m; if(j < N/2) {//buttom right r.push_back(make_pair(mod(y-x,m),t)); } else {//top right r.push_back(make_pair(mod(m-(y+x),m),t)); } } } printf("%d\n" , ans); } return 0; }
|
|
常用链接
随笔分类
Links
搜索
最新评论
|
|