JOI 2012 fish 题解
题目大意:
给你一个0/1/2序列Ai,每个值Ai有一个权值Pi。如果两个值的权值Pi和Pj满足Pi≥2Pj,那么Ai就会把Aj吔掉,也就是说Ai, Aj不能共存。
称Ai的一个子序列的特征三元组为(Sum0, Sum1, Sum2),其中Sump为子序列中的Ai=p的个数。当然这里要求所有值可以共存。
求:Ai的所有合法子序列的特征三元组的种类数目。1≤N≤200000。
这道题可谓是难题(对于我),同样是听完题解外加问人外加参考代码才回的,我们给个原题网址(日文版)戳一下http://www.ioi-jp.org/camp/2012/2012-sp-tasks/
首先我们现将p从小到大排一下序,此时我们可以求出对于每一个i它的极大三元组,就是对于每一个i,求一个最大的j,使得pj<pi*2,那么统计i到j中的0,1,2的个数,记它为极大三元组,我们记这n个极大三元组为a,b,c。
如果将a,b,c放入立体三维空间中,那么问题就转换为了求这些立方体(0,0,0到a,b,c)的体积并,只要在立方体里面的点就一定满足条件,这显然是比较难求的,但是如果我们把它转换为二维,问题就简单了很多。
首先我们将它们以a从小到大排序,然后依次在平面内加入点(b,c),我们可以发现,对于任意a'>a,满足a'的所有b,c一定在a下一定成立,所以每次在统计a的时候,要加上之前>a的面积并。并且我们发现,每一时刻面积并的形状都是阶梯状的,也就是说,b越大c越小,所以可以每次加进来一个点,只需要判断它是否在图形外,在图形外,则要更新面积以及在边缘的点,这就体现了c++的好处,巧用set,就可以避免写平衡树了。贴几张原题题解图
+-----+-----+
/ / /|
+-----+-----+ |
/ / /| +
+-----+-----+ |/|
| | | + |
| | |/| +
+-----+-----+ |/|
| | | + |
| | |/| +
+-----+-----+ |/
| | | +
| | |/
+-----+-----+
+-----+-----+
/0,2,1/1,2,1/|
+-----+-----+ |
/ / /| +
+-----+-----+ |/|
| | | + |
|0,2,0|1,2,0|/| +
+-----+-----+ |/|
| | | + |
|0,1,0|1,1,0|/| +
+-----+-----+ |/
| | | +
|0,0,0|1,0,0|/
+-----+-----+
+---------+
/ /|
/ / |
+---------+ |
| | +--------+
+------+ | / /|
/ /| |/ / +-----+
/ / ;--+ / / /|
+------+ +-----------+ +-----+ +
| | | | | |/
| | | | +-----+
| | | |/
| | +-----------+
| |/
+------+
+--+--+----+----+---+-->
| | | | | |
+--+--+----+----+---@
| | | | |
+--+--+----@ |
| | | |
+--+--+---------@
+--+--@
| |
| |
+--@
|
V
+-------------------+-->
| |
| +---@
| |
| |
| |
| +---------@
| +--@
| |
| | o <-- たとえばこの点を追加するとします
+--@
|
V
+-------------------+-->
| |
| +-+-@
| | |
| | |
| | |
| +---------@ |
| +--@ |
| | |
| +--------------o
+--@
|
V
+-------------------+-->
| |
| +-@
| |
| |
| |
| ; |
| ; |
| |
| +--------------o
+--@
|
V
可能讲的不清楚,具体看代码吧。
1 #include<iostream> 2 #include<set> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 int n; 7 const int maxn=200005; 8 struct node 9 { 10 int type,val; 11 }a[maxn]; 12 struct three 13 { 14 int a,b,c; 15 }d[maxn]; 16 bool cmp(node u,node v) 17 { 18 return u.val<v.val; 19 } 20 int now; 21 bool ff(three u,three v) 22 { 23 return u.a>v.a; 24 } 25 long long mian; 26 set <int> s; 27 int h[maxn],l[maxn],r[maxn]; 28 void listAdd(int x, int prev) 29 { 30 int succ=r[prev]; 31 l[x]=prev;r[prev] = x; 32 r[x]=succ;l[succ] = x; 33 } 34 long long ans; 35 int main() 36 { 37 scanf("%d",&n); 38 for(int i=1;i<=n;i++)scanf("%d%d",&a[i].type,&a[i].val); 39 sort(a+1,a+n+1,cmp); 40 // for(int i=1;i<=n;i++)cout<<a[i].val<<" "; 41 int da=0,db=0,dc=0; 42 for(int i=1;i<=n;i++) 43 { 44 if(i!=1) 45 { 46 if(a[i-1].type==0)da--; 47 else if(a[i-1].type==1)db--; 48 else dc--; 49 } 50 while(now<n && a[now+1].val<a[i].val*2) 51 { 52 now++; 53 if(a[now].type==0)da++; 54 else if(a[now].type==1)db++; 55 else dc++; 56 } 57 // cout<<da<<" "<<db<<" "<<dc<<endl; 58 d[i].a=da+1; 59 d[i].b=db+1; 60 d[i].c=dc+1; 61 } 62 sort(d+1,d+n+1,ff); 63 64 for(int i=1;i<=n;i++) 65 // printf("%d %d %d\n",d[i].a,d[i].b,d[i].c); 66 67 s.clear(); 68 int ll=0,rr=n+2; 69 l[ll]=l[rr]=ll; 70 r[ll]=r[rr]=rr; 71 h[ll]=rr; 72 h[rr]=ll; 73 s.insert(ll); 74 s.insert(rr); 75 for(int i=1;i<=n;i++) 76 { 77 ans+=mian*(long long)(d[i-1].a-d[i].a); 78 // cout<<ans<<endl; 79 80 int x=d[i].b; 81 int y=d[i].c; 82 if(!s.count(x)) 83 { 84 int succ=*s.lower_bound(x); 85 s.insert(x); 86 listAdd(x,l[succ]); 87 h[x]=h[succ]; 88 } 89 while(true) 90 { 91 if(h[x]>=y)break; 92 mian+=((long long)y-h[x])*((long long)x-l[x]); 93 h[x]=y; 94 x=l[x]; 95 } 96 //cout<<i<<endl; 97 x=d[i].b; 98 while(h[l[x]]==h[x]) 99 { 100 int tx=l[x]; 101 r[l[tx]]=r[tx]; 102 l[r[tx]]=l[tx]; 103 s.erase(tx); 104 } 105 // cout<<i<<endl; 106 //cout<<"find"<<endl; 107 } 108 ans+=mian*d[n].a; 109 cout<<ans<<endl; 110 return 0; 111 }