P2403 [SDOI2010]所驼门王的宝藏
P2403 [SDOI2010]所駝門王的寶藏
題意:
R * C的地圖上有n個寶藏,給你n個寶藏的坐標,每個寶藏的位置上還有一個傳送門,傳送門有三種類型,1.可以傳送到同行的其他寶藏位置,2.可以傳送到同列的其他寶藏位置 3.可以傳送到該點周圍的八個位置
你可以在任意一個寶藏處開始,問最多獲得多少寶藏?
題解:
如果我們直接按照題意要求建邊,(暴力建邊,第1類門和每行其他門建邊,第2,3類門同理),會得到一個有向有環圖,對于環我們可以用tarjan進行縮點,然后得到DAG,然后就是在DAG直接dp求就行(拓撲排序)。
dp[v]=max(dp[u]+val[v])dp[v]=max(dp[u]+val[v] )dp[v]=max(dp[u]+val[v])
但是數據告訴我們不會這么簡單,N<=100000,R<=1000000,C<=1000000
邊的數量巨大,可達O(n2n^2n2),所以需要一個巧妙的建邊方法來降低復雜度
原始的建邊是兩兩之間建立練習,避免不了是O(n2n^2n2),現在我們可以這樣想,對于每行,每列都建立一個額外的點,然后讓所有點與這個額外的點建立聯系。
第i行的額外的點為x,我們讓x向改行所有寶藏建一個邊,如果有一個點y是1號門,我們就讓y再向x建一個邊,通過這樣操作,y就是本行任意一個寶藏位置(很妙,這個思路很妙)
列的同理
對于第三種門,我們就暴力枚舉八個位置,然后在所有寶藏中二分尋找(事先要對寶藏位置排序),看是否能找到,找到即建邊
思路捋清楚,就開干碼吧
代碼:
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read(){ll s=0,w=1ll;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w; } clock_t startTime, endTime; void rd_test(){#ifdef ONLINE_JUDGE#elsestartTime = clock(); //計時開始freopen("sotomon.in","r",stdin);#endif } void Time_test(){#ifdef ONLINE_JUDGE#elseendTime = clock(); //計時結束printf("\n運行時間為:%lfs\n",(double)(endTime - startTime) / CLOCKS_PER_SEC);#endif } const int N = 2e5+9, T = 2100007, M = 1000007; const int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1}; const int dy[8] = {1, 0, -1, 1, -1, 1, 0, -1}; int n, r, c, t, edc, edu[M], edv[M]; int ecnt, head[T], nxt[M], vet[M]; int qhead, qtail, que[T], f[T], in[T]; int stac[T], top, val[T], col[T], stamp, dfn[T], low[T], cnt; bool instac[T]; struct Node {int x, y, t;bool operator <(const Node &ano) const { return x < ano.x || x == ano.x && y < ano.y;} } a[N]; inline void add(int u, int v) {edu[++edc] = u; edv[edc] = v;vet[++ecnt] = v; nxt[ecnt] = head[u];head[u] = ecnt; }inline void eadd(int u, int v) {vet[++ecnt] = v; nxt[ecnt] = head[u];head[u] = ecnt; }void tarjan(int u) {dfn[u] = low[u] = ++stamp;stac[++top] = u; instac[u] = true;for (int e = head[u]; e; e = nxt[e]) {int v = vet[e];if (!dfn[v]) {tarjan(v); low[u] = min(low[u], low[v]);} else if (instac[v]) low[u] = min(low[u], dfn[v]);}if (dfn[u] == low[u]) {col[u] = ++cnt; val[cnt] = u > r + c;while (stac[top] != u) {col[stac[top]] = cnt;val[cnt] += (stac[top] > r + c);instac[stac[top--]] = false;}instac[stac[top--]] = false;} }int getid(int x, int y) {int l = 1, r = n;while (l <= r) {int mid = (l + r) >> 1;if (a[mid].x == x && a[mid].y == y) return mid;else if (a[mid].x < x || a[mid].x == x && a[mid].y < y) l = mid + 1;else r = mid - 1;}return -1; }int main() {rd_test();scanf("%d%d%d", &n, &r, &c);for (int i = 1; i <= n; ++i) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].t);sort(a + 1, a + 1 + n);for (int i = 1; i <= n; ++i) {//處理額外點對當前點的連邊add(a[i].x, r + c + i); add(r + a[i].y, r + c + i);//處理當前點對其它點的連邊if (a[i].t == 1) add(r + c + i, a[i].x);else if (a[i].t == 2) add(r + c + i, r + a[i].y);else {for (int k = 0; k < 8; ++k) {int x = a[i].x + dx[k], y = a[i].y + dy[k];if (x >= 1 && x <= r && y >= 1 && y <= c) {int id = getid(x, y);if (id != -1) add(r + c + i, r + c + id);} }}}//縮點 t = r + c + n; for (int i = 1; i <= t; ++i)if (!dfn[i]) tarjan(i);ecnt = 0; memset(head, 0, sizeof(head));for(int i = 1; i <= edc; ++i)if (col[edu[i]] != col[edv[i]]) {eadd(col[edu[i]], col[edv[i]]);++in[col[edv[i]]];}//拓撲排序qhead = 0; qtail = -1;for (int i = 1; i <= cnt; ++i) if (!in[i]) {que[++qtail] = i;f[i] = val[i];}while (qhead <= qtail) {int u = que[qhead++];for (int e = head[u]; e; e = nxt[e]) {int v = vet[e];f[v] = max(f[v], f[u] + val[v]);if (--in[v] == 0) que[++qtail] = v;}}//統計答案int ans = 0;for (int i = 1; i <= cnt; ++i)ans = max(ans, f[i]);printf("%d\n", ans);Time_test();return 0; }總結
以上是生活随笔為你收集整理的P2403 [SDOI2010]所驼门王的宝藏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 硬盘如何储存数据硬盘如何储存数据和信息
- 下一篇: 可持久化4--可持久化并查集