zoukankan      html  css  js  c++  java
  • Topcoder SRM 602 div1题解

    打卡~

    Easy(250pts):

    题目大意:rating2200及以上和2200以下的颜色是不一样的(我就是属于那个颜色比较菜的),有个人初始rating为X,然后每一场比赛他的rating如果增加就会加D[i],如果减就会减D[i],但是如果rating小于0了就变成0,这个人不太喜欢比较厉害的颜色,所以他不能连续两次rating大于等于2200,求颜色变化的最多个数,保证比赛最多50场,D[i]<=10^9。

    这套题还是很难的,所以我们来好好分析。

    首先,样例0告诉我们贪心什么都是假的,不存在的,所以只能DP咯。

    我们考虑f[i][j]表示第i场比赛打完rating是j的颜色变化最大值,

    如果掉rating,那么最优值就是f[i+1][max(j-D[i],0)],

    如果涨rating,又没涨到2200,那么最优值就是f[i+1][j+D[i]],

    如果涨rating而且涨到2200,那么如果这是最后一场比赛直接返回1,不然就一定要求j+D[i]-D[i+1]<2200,于是最优值就是f[i+2][j+D[i]-D[i+1]]+2,

    这样就能DP了,然而D[i]的范围太大了,显然会爆炸。

    我们考虑如何减少状态数,于是我们发现当rating高于2200的时候,下一场必须跌而且必须跌到2200以下,

    所以其实如果rating大于2200,那么我们根本不用关心他的rating是多少,

    这样就可以省好多好多状态啦,然后直接dp就好了。

    时间复杂度O(n*w),代码如下:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int n;
     4 int f[57][2207];
     5 class TypoCoderDiv1
     6 {
     7     public:
     8     int getmax(vector <int> D, int X)
     9     {
    10         n=D.size();
    11         memset(f,0,sizeof(f));
    12         for (int i=n-1;i>=0;i--)
    13             for (int j=0;j<2200;j++)
    14             {
    15                 if (j+D[i]<2200) f[i][j]=f[i+1][j+D[i]]; else
    16                 if (i+1==n) f[i][j]=1; else
    17                 if (j+D[i]-D[i+1]<2200) f[i][j]=f[i+2][max(0,j+D[i]-D[i+1])]+2;
    18                 f[i][j]=max(f[i][j],f[i+1][max(0,j-D[i])]);
    19             }
    20         return f[0][X];
    21     }
    22 };

    Medium(550pts):

    题目大意:有2N个长方形,每个长方形的长和宽都给定,现在要使得这些长方形恰好分成两组,每组N个长方形,把一组堆在桌上,最大重合面积分别为S1,S2,要求输出S1+S2的最大值,数据满足N<=100000,长宽都在10^9范围内。

    种子要先预处理出来,这个没有异议的吧。。。

    我们先考虑一个比较简化的问题,我们不考虑长方形的分组,只考虑N个长方形怎么叠在一起S最大。

    显然我们需要把这N个长方形按照一个边角对齐,然而只看出这一个性质的话只能过div2的medium,

    我们还需要看出,对于每个长方形一定是每个长方形较短的边堆放在一个方向,较长的边堆放在另一个方向,我们来证明一下。

    假设两个长方形,第一个为x*y(x<=y),第二个为z*w(z<=w),并且有x<=z,

    如果较短的边都在一个方向,那么S=min(x,z)*min(y,w)=x*min(y,w),

    如果较短的边不在一个方向,那么S=min(x,w)*min(y,z)=x*min(y,z),

    显然有min(y,w)>=min(y,z),

    所以这个结论是正确的~~~

    接下来,我们来考虑如何分组,

    通过上面两个式子会发现,不管怎么分组,最短的那条x边一定会成为某个S的一条边,

    于是我们先令所有长方形的x<=y,然后将所有长方形按照x从小到大排序,

    那么x[1]一定会被选中,我们假设另一个会被选中的x边为x[i],

    我们先枚举i,那么显然1~i-1都被分在了一组,还剩下i+1~2*N没有被分组,一共2*N-i个长方形。

    这2*N-i个长方形中y最小的那个长方形的y值设为y0,于是y0要么和x[1]一组,要么和x[i]一组,

    由于我们要求S的和最大,那么剩下的一个y显然要取那2*N-i个长方形中y值最大的那个y值。

    于是经过n次的枚举,我们得到了最后的答案。

    由于n<=100000,直接O(n^2)暴力显然不行(听官方题解说暴力是O(n^2logn)),所以我们需要一个随便什么数据结构来维护一下就可以了。

    当然最简单的方法就是直接上两个multiset就完事啦~

    时间复杂度O(nlogn),代码如下:

     1 #include <bits/stdc++.h>
     2 #define Maxn 200007
     3 #define inf 10000000007
     4 using namespace std;
     5 int n;
     6 long long x[Maxn],y[Maxn];
     7 pair<long long,long long> a[Maxn];
     8 multiset<long long> st1,st2;
     9 multiset<long long>::iterator it;
    10 class PilingRectsDiv1
    11 {
    12     public:
    13     long long getmax(int N, vector <int> XS, vector <int> YS, int XA, int XB, int XC, int YA, int YB, int YC)
    14     {
    15         n=N;
    16         for (int i=0;i<XS.size();i++) x[i]=XS[i],y[i]=YS[i];
    17         for (int i=XS.size();i<2*n;i++) x[i]=(1LL*x[i-1]*XA+XB)%XC+1,y[i]=(1LL*y[i-1]*YA+YB)%YC+1;
    18         for (int i=1;i<=2*n;i++) a[i]=make_pair(min(x[i-1],y[i-1]),max(x[i-1],y[i-1]));
    19         sort(a+1,a+2*n+1);
    20 //a[1].x must be chosen
    21         st1.clear(),st2.clear();
    22         for (int i=1;i<=n;i++) st1.insert(a[i].second),st2.insert(a[i+n].second);
    23         long long ans=0;
    24         for (int i=n;i;i--)
    25         {
    26 //there will be only n kind of plans
    27 //i means that the ith block has the smallest x
    28             ans=max(ans,1LL*a[1].first*(*st1.begin())+1LL*a[i+1].first*(*st2.begin()));
    29             st1.erase(st1.find(a[i].second)),st2.insert(a[i].second);
    30             st1.insert(*st2.begin()),st2.erase(st2.begin());
    31         }
    32         st1.clear(),st2.clear();
    33         for (int i=1;i<=n;i++) st1.insert(a[i].second),st2.insert(a[i+n].second);
    34         for (int i=n;i;i--)
    35         {
    36             ans=max(ans,1LL*a[1].first*(*st1.begin())+1LL*a[i+1].first*(*st2.begin()));
    37             st1.erase(st1.find(a[i].second)),st2.insert(a[i].second);
    38             it=st2.end();--it;
    39             st1.insert(*it),st2.erase(it);
    40         }
    41         return ans;
    42     }
    43 };

    Hard(1000pts):

    这应该是我做到的SRM里面第一个分值1000分的hard题,好神哪>_<

    这个题是一个超级帅无敌的公式题,但是似乎官方题解中并没有把推导过程写清楚。

    题目大意:有一个N*M的方格组成的小黑盒,每个黑盒中的格子都是一块倾斜45度的玻璃板(有两个方向),现在从外围2(N+M)个格子打入激光,激光一定会退出小黑盒,这样我们得到了2(N+M)个格子和它们对应的出口。现在从小黑盒中任意去掉一块玻璃板,从外围2(N+M)个格子打入激光,发现它们对应的出口完全没有变化。求满足题意的玻璃板构成的方案数。数据满足1<=N,M<=200。

    考虑对于每一块玻璃的每一个面,如果它被同一道激光照射了两次,那么说明激光形成了一个圈,由光路可逆,这光线就出不来了。。。

    所以每一面最多被照射过一次,所以每一块玻璃被照射过了0次或者1次或者2次。

    我们定义如果有一部分玻璃形成了一块封闭区域,而且光线如果在里面会形成循环,那么定义这一组玻璃为一个圈,

    如果某一块玻璃板旋转90度后能组成一个圈,那么定义这组玻璃是一个半圈。

    如果一块玻璃板被照射过了0次,直接拿走它显然不会造成任何问题,所以我们把它拿(chi)走(diao)。

    如果一块玻璃板被照射了1次,我们假设光线从上射入,从左反射出。

    那么我们将这块玻璃拿走,光线穿过方格到下方,通过一系列变化来到右方,从右往左穿方格。

    也就是说,这块玻璃在一个圈中。

    如果一块玻璃被照射了2次,我们假设光线从上射入,从左反射出,再到下方,从下进入,从右反射出。

    我们将这块玻璃拿走,光线从上射入,从下穿过,再来到左方,从右侧穿过。

    也就是说,如果把这块玻璃旋转90度,那么它在一个圈中,换句话说这块玻璃在一个半圈中。

    问题转化为了,有多少方案使得图中存在圈或者半圈。

    随手画几个圈和半圈就能够发现,一个图中如果有圈,就一定存在样例0中的5个基本图形。

    问题转化为了,有多少方案使得图中至少存在5个基本图形之一。

    到这里官方题解写了这么一句话:“The approach here is to do further analysis and find the following formula”,然后给出了一个公式。。。

    这个公式反正我是不会证明的,也许找规律能找出来吧。。。

    matthew99a对这个化简的问题使用了dp,但是我也没看懂,所以这里直接给出公式。(详见代码)

    时间复杂度写什么啊。。。O(log(N*M))/O(N*M),代码如下:

     1 #include <bits/stdc++.h>
     2 #define modp 1000000007
     3 #define Maxn 
     4 using namespace std;
     5 int power[40007];
     6 class BlackBoxDiv1
     7 {
     8     public:
     9     int count(int N, int M)
    10     {
    11         power[0]=1;
    12         for (int i=1;i<=M*N;i++) power[i]=(2LL*power[i-1])%modp;
    13         long long ans=power[N*M]-1LL*power[N]*M*M%modp-1LL*power[M]*N*N%modp+modp+modp;
    14         ans=(ans+1LL*(N-1)*(N-2)*(M-1)*(M-2)/2%modp+2LL*N*M*(N+M)%modp-5LL*N*M%modp+3*N+3*M-3+modp+modp+modp)%modp;
    15         return ans;
    16     }
    17 };
  • 相关阅读:
    什么是 bean 的自动装配?
    什么是 Spring 的内部 bean?
    什么是 Spring 的 MVC 框架?
    Spring AOP and AspectJ AOP 有什么区别?
    解释 JDBC 抽象和 DAO 模块?
    volatile 类型变量提供什么保证?
    一个 Spring Bean 定义 包含什么?
    什么是 Spring MVC 框架的控制器?
    使用 Spring 访问 Hibernate 的方法有哪些?
    什么是 Callable 和 Future?
  • 原文地址:https://www.cnblogs.com/Tommyr7/p/6807216.html
Copyright © 2011-2022 走看看