题意:
许多 k 维点, 求这些点之间的最远曼哈顿距离. 并且有 q 次操作, 插入一个点或者删除一个点. 每次操作之后均输出结果.
思路:
用"疑似绝对值"的思想, 维护每种状态下各点的计算值, 插入或删除一个点就更新一次每种状态(用 multiset 或 map 或 priority_queue 实现), 每次求ans时扫一遍最大差值即可.
为了练习STL, 每一个都实现一次.
/* ********************************************** Author : kuangbin Created Time: 2013/8/13 18:25:38 File Name : F:\2013ACM练习\2013多校7\1001.cpp *********************************************** */ //4640MS 14972K #include <cstdio> #include <algorithm> #include <set> using namespace std; int a[60010][10]; multiset<int>mst[1<<5]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int q,k; while(scanf("%d%d",&q,&k)==2) { for(int i = 0;i < (1<<k);i++) mst[i].clear(); int od,x; for(int i = 1;i <= q;i++) { scanf("%d",&od); if(od == 0) { for(int j = 0;j < k;j++) scanf("%d",&a[i][j]); for(int j = 0; j < (1<<k); j++) {//计算当前点在每种情况下的"疑似绝对值" int s = 0; for(int t = 0; t < k;t++) if(j & (1<<t)) s += a[i][t]; else s -= a[i][t]; mst[j].insert(s);//插入到该种情况下 } } else { scanf("%d",&x); for(int j = 0; j < (1<<k); j++) {//一次操作,插入或删除一个点,都是将这个点对应的所有状态插入每种状态中 int s = 0;//因此,要清除一次操作,就要删除所有状态中的那一个 for(int t = 0; t < k;t++) if(j & (1<<t)) s += a[x][t]; else s -= a[x][t]; multiset<int>::iterator it = mst[j].find(s); mst[j].erase(it); } } int ans = 0; for(int j = 0; j < (1<<k);j++) { multiset<int>::iterator it = mst[j].end(); it--; int t1 = (*it); it = mst[j].begin(); int t2 = (*it);//用于作差 ans = max(ans,t1-t2);//保留最大值 } printf("%d\n",ans); } } return 0; }
//8359MS 37928K慢死了 #include <cstdio> #include <algorithm> #include <map> using namespace std; int a[60010][6]; map<int, int> mp[1<<5]; int main() { int q,k; while(scanf("%d %d",&q,&k)==2) { for(int i=0;i<1<<k;i++) mp[i].clear(); int od, x; for(int i=1;i<=q;i++) { scanf("%d",&od); if(!od) { for(int j=0;j<k;j++) scanf("%d",a[i]+j); for(int s=0;s<1<<k;s++) { int t = 0; for(int j=0;j<k;j++) { if(s & (1<<j)) t += a[i][j]; else t -= a[i][j]; } mp[s][t]++; // printf("map[s][t] = %d\n",mp[s][t]); } } else { scanf("%d",&x); for(int s=0;s<1<<k;s++) { int t = 0; for(int j=0;j<k;j++) { if(s & (1<<j)) t += a[x][j]; else t -= a[x][j]; } map<int, int>::iterator it = mp[s].find(t); mp[s][t]--; } } int ans = 0; for(int s=0;s<(1<<k);s++) { map<int, int>::iterator it = mp[s].end(); it--; while(it->second==0) it--; int mx = it->first;///first~~~ it = mp[s].begin(); while(it->second==0) it++; int mi = it->first; ans = max(ans, mx - mi); // printf("mx = %d, mi = %d\n",mx,mi); } printf("%d\n",ans); } } }
优先队列只能返回队首元素,因此需要两个队列分别求最大最小值.
对于已删除的元素, 无法直接删除, 可以做标记, 碰到已删除的元素时直接pop掉就行了.
因此入队的就不能仅仅是一个值(前两个有find功能, 不需要额外标号), 而应该是一个记录key和value的结构体.
//2218MS 36748K #include <cstring> #include <cstdio> #include <algorithm> #include <queue> using namespace std; int a[6]; bool vis[60005]; typedef struct ascending_node { int id,t; bool operator<(const ascending_node& a) const { return t > a.t; } }anode; typedef struct descending_node { int id,t; bool operator<(const descending_node& a) const { return t < a.t; } }dnode; /* 2812MS 30224K priority_queue<anode> apq[1<<5]; priority_queue<dnode> dpq[1<<5]; int main() { int q,k; while(scanf("%d %d",&q,&k)==2) { for(int i=0;i<1<<k;i++) { while(!apq[i].empty()) apq[i].pop(); while(!dpq[i].empty()) dpq[i].pop(); }*/ /**/ int main() { int q,k; while(scanf("%d %d",&q,&k)==2) { priority_queue<anode> apq[1<<5]; priority_queue<dnode> dpq[1<<5];/**/ anode t1; dnode t2; memset(vis,false,sizeof(vis)); int od, x; for(int i=1;i<=q;i++) { scanf("%d",&od); if(!od) { for(int j=0;j<k;j++) scanf("%d",a+j); for(int s=0;s<1<<k;s++) { int t = 0; for(int j=0;j<k;j++) { if(s & (1<<j)) t += a[j]; else t -= a[j]; } t1.t = t2.t = t; t1.id = t2.id = i; apq[s].push(t1); dpq[s].push(t2); // printf("map[s][t] = %d\n",mp[s][t]); } } else { scanf("%d",&x); vis[x] = true; } int ans = 0; for(int s=0;s<(1<<k);s++) { while(1) { t1 = apq[s].top(); if(!vis[t1.id]) break; apq[s].pop(); } while(1) { t2 = dpq[s].top(); if(!vis[t2.id]) break; dpq[s].pop(); } ans = max(ans, t2.t-t1.t); } printf("%d\n",ans); } } }