Link:

Codeforces #172 传送门

A:

一眼看上去分两类就可以了

1、每个矩形只有两条边相交,重合的形状为菱形

2、每个矩形四条边都有相交

对于情况1答案为$h*h/sin(a)$

对于情况2可以列出一个二元一次方程组,手动解一下就好了

不过计算几何确实容易写挂啊……

有几个注意点:

1、对于情况1$h$指的是较短的边,不符合时要交换$w,h$

以后处理长方形时还是要注意,不能只看样例啊

2、在判断情况1,2的临界点时用$sin$来判断,因为此时$a$已经全转为锐角了

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define PI M_PI
typedef long long ll;
typedef pair<int,int> P;
double w,h,a,x,y,res; int main()
{
scanf("%lf%lf%lf",&w,&h,&a);
if(!a||a>=) return printf("%.9lf",w*h),;
if(a>) a=-a;
if(h>w) swap(w,h);//情况1要求h是短边
a=a*PI/;
if(sin(a)>=sin(*atan2(w,h)))//由于角度已改变,用sin判断
res=h*h/sin(a);
else
y=(h*(cos(a)+)-w*sin(a))/((cos(a)+)*(cos(a)+)-sin(a)*sin(a)),
x=(h-y*(cos(a)+))/sin(a),res=w*h-(x*x+y*y)*sin(a)*cos(a);
printf("%.9lf",res);
return ;
}

Problem A

其它方法:

1、现场大部分人都是暴力半平面交做的……

如果怕少考虑特解这样确实能大大降低写挂概率

2、对于情况2也可以不用解方程

其中三角形的直角边和长/宽一半的差是能直接算的,这样好像更简单一些

B:

一开始全在想异或性质的应用……

但后来发现此题仅和如何枚举所有区间的最大+次大值有关

其实就是要想到一种能滤去所有“无用区间”的方式

考虑无用区间产生的原因就是往最大/次大值两侧扩展了更小的值

如果考虑当前值所有可行的次大值,则其一定是从右向左递增的

这样用一个单调栈维护,每次新加进一个数和 比其小的数+第一个比其大的数 的异或值更新答案

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef pair<int,int> P;
const int MAXN=1e5+;
int n,x,st[MAXN],top,res; int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d",&x);
while(top&&x>st[top])
res=max(res,x^st[top--]);
if(top) res=max(res,x^st[top]);
st[++top]=x;
}
printf("%d",res);
return ;
}

Problem B

C:

感觉自己还是有期望恐惧症啊……

此题实为一个结论题,结论为$\sum \frac{1}{dep[i]}$

这其实是利用了期望是线性的这一性质,即$E(x+y)=E(x)+E(y)$

有了该性质就可以单独考虑每个点被直接删除的期望,再相加即可

一个点最终被删除当且仅当其到根的路径上的任意一点被删除

因此其直接删除该点的期望为$\frac{1}{dep[i]}$,最后再求和

#include <bits/stdc++.h>

using namespace std;
const int MAXN=1e5+;
vector<int> G[MAXN];
int n,x,y;double res; void dfs(int x,int anc,int dep)
{
res+=1.0/dep;
for(int i=;i<G[x].size();i++)
if(G[x][i]!=anc) dfs(G[x][i],x,dep+);
} int main()
{
scanf("%d",&n);
for(int i=;i<n;i++)
scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
dfs(,,);
printf("%.6lf",res);
return ;
}

Problem C

D:

此题将一类经典问题加上了修改和动态查询

该经典问题是指:在$n$个数中取$k$段,使得最终和最大

先考虑静态问题,发现是可以直接$dp$的

用$dp[i][j][0/1]$表示前$i$个数中取$j$段的最大和,同时1表示最后一个数必须取

这样的复杂度为$O(n*k)$

不过这个复杂度是可以用fhq提出的增量法优化的:

每次取当前和最大的一段,同时将该段中的数全部取反。重复$k$次

其中的取反操作其实就是提供了“反悔”机制,如果后面有一段跨过了取反的段就说明这一段不再取了

同时取反后的数依然满足求最大和的目的,毕竟加上了最大取反的数就是少减去了最大的数

对于上面两个操作明显可以用线段树来实现,复杂度是$O(k*log(n))$

这个方法同样可以用费用流来理解

如果对原问题建图将是$<S,i,1,0><i,T,1,0><i,i+1,1,a[i]>$

发现上面的增量法其实就是优化了跑该网络流的过程

接下来用上面的方法来解决动态问题

对于朴素的$dp$方法,对每个点记录$f[k][0/1/2/3]$

表示在该点表示的区间中取$k$段的最大值,限制分别为头必取/尾必取/头尾都取/无限制

但这样将两个区间合并需要$O(k^2)$,总复杂度为$O(q*k^2*log(n))$,想过要稍微卡卡常

但是增量法可以将总复杂度降为$O(q*k*log(n))$,原因在于线段树中每个点都只要保留取1段的最大值

每次的区间合并是$O(1)$的,不过对于每次查询要进行$k$次求最大和、区间取反、撤销

对于每个点记录$mx,lmx,rmx$分别表示取一段的最大和、最大前缀和、最大后缀和

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
#define lc ls,l,mid
#define rc rs,mid+1,r
typedef long long ll;
typedef pair<int,int> P;
typedef double db;
const int MAXN=1e5+;
int top,L[MAXN],R[MAXN];
int n,m,dat[MAXN],rev[MAXN<<],op,l,r,k,res;
struct SGT
{
int l,r,lm,rm;
int lmx,rmx,mx,sum;
void mdy(int val,int pos)
{
sum=val;
l=r=lm=rm=pos;
lmx=rmx=mx=(val>?val:);
}
void upd(SGT a,SGT b)
{
sum=a.sum+b.sum;
lmx=a.lmx;lm=a.lm;rmx=b.rmx;rm=b.rm;
if(a.mx>b.mx) mx=a.mx,l=a.l,r=a.r;
else mx=b.mx,l=b.l,r=b.r; if(a.rmx+b.lmx>mx) mx=a.rmx+b.lmx,l=a.rm,r=b.lm;
if(a.sum+b.lmx>lmx) lmx=a.sum+b.lmx,lm=b.lm;
if(a.rmx+b.sum>rmx) rmx=a.rmx+b.sum,rm=a.rm;
}
}seg[MAXN<<][],ret,tmp; void Pushup(int k)
{
for(int i=;i<;i++)
seg[k][i].upd(seg[ls][i^rev[ls]],seg[rs][i^rev[rs]]);
}
void Update(int pos,int val,int k,int l,int r)
{
if(l==r)
{
seg[k][].mdy(val,pos);
seg[k][].mdy(-val,pos);
return;
}
if(pos<=mid) Update(pos,val,lc);
else Update(pos,val,rc);
Pushup(k);
}
void Modify(int a,int b,int k,int l,int r)
{
if(a<=l&&r<=b){rev[k]^=;return;}
if(a<=mid) Modify(a,b,lc);
if(b>mid) Modify(a,b,rc);
Pushup(k);
}
void Query(int a,int b,int k,int l,int r,int f)
{
f^=rev[k];//永久化标记
if(a<=l&&r<=b)
{
if(l==a) ret=seg[k][f];
else tmp.upd(ret,seg[k][f]),ret=tmp;
return;
}
if(a<=mid) Query(a,b,lc,f);
if(b>mid) Query(a,b,rc,f);
} int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&dat[i]),Update(i,dat[i],,,n);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&op,&l,&r);
if(op==) dat[l]=r,Update(l,r,,,n);
else
{
scanf("%d",&k);
top=;res=;
while(top<k)
{
Query(l,r,,,n,);
if(ret.mx<=) break;
res+=ret.mx;L[++top]=ret.l;R[top]=ret.r;
Modify(L[top],R[top],,,n);
}
printf("%d\n",res);//对更改进行撤销
while(top) Modify(L[top],R[top],,,n),top--;
}
}
return ;
}

Problem D

此题有一些技巧是要注意的

1、翻转操作是可以用永久化标记的!

对每个点同时记录两种状态下的值,查询时用一个值记录当前实际是哪种状态即可

只要修改操作和值无关一般都可以不用$pushdown$,尽量用永久化标记

2、线段树对多段问题的更新上传

对于多段问题每个节点都要记录两个边界是否选取来确定合并之后的段数,算是个套路吧

E:

$Seter$出的神题……

普通的$dp$非常好想,用$dp[i][j]$表示第$i$个数选$j$的代价,$f[i][j]$表示$min(dp[i-1][j-k]),k\in[a,b]$

那么$dp[i][j]=2*(j-dat[i])+f[i][j]$

但其时间/空间复杂度都和数的值域相关,需要优化

接下来就是神级的单调性优化了

为了将$dp$和值域去相关,可以将$dp[i],f[i]$看成和$j$相关的函数!

接下来对$dp[i],f[i]$求导,同时由于$dp[1]'$单调,假设$dp[i]'$单调,且零点为$k$,则:

$dp[i+1]'(j)=2*(j-dat[i+1])+f[i+1]'(j)$,$f[i+1]'(j)=dp[i]'(j-a)(j<k+a),0(k+a\le j\ge k+b),dp[i]'(j-b)(j>k+b)$

可以发现每次$f[i+1]'$是将$dp[i]'$的函数中$[1,k)$向右移$a$,$(k,q]$向右移$b$,然后中间再加上一段零

而每次$dp[i+1]'$是在$f[i+1]'$的基础上加上一个单调递增的函数,因此$dp[i]'$单调增则$dp[i+1]'$单调增

证明了$dp[i]'$的单调性,且发现每次在原函数的基础上仅增加了一段一次函数

因此只要模拟上述过程,记录至多$n$段函数,就能最终在$O(n)$的时间内找到零点,即当前最优解

接下来考虑如何输出方案:

对于每个$i$记录下当前的零点位置

在求出$n$的零点后向回逆推每次的选择即可

#include <bits/stdc++.h>

using namespace std;
#define X first
#define Y second
typedef long long ll;
typedef double db;
typedef pair<db,db> P;
const int MAXN=1e4+;
P dp[MAXN*];int n,cp,tot;
db q,a,b,zp,dat[MAXN],res[MAXN],sum; int main()
{
scanf("%d%lf%lf%lf",&n,&q,&a,&b);
for(int i=;i<=n;i++) scanf("%lf",&dat[i]);
dp[++tot]=P(,*(-dat[]));
dp[++tot]=P(q+,*(q+-dat[]));
res[]=zp=dat[];cp=;
for(int i=;i<=n;i++)
{
for(int j=tot;j>cp;j--) dp[j+]=dp[j];
tot+=;dp[cp+]=P(zp+a,);dp[cp+]=P(zp+b,); for(int j=;j<=cp;j++) dp[j].X+=a;
for(int j=cp+;j<=tot;j++) dp[j].X+=b;
for(int j=;j<=tot;j++) dp[j].Y+=*(dp[j].X-dat[i]); if(dp[].Y>=) cp=,zp=dp[].X;
else
{//找零点
for(cp=;cp<tot;cp++)
if(dp[cp].Y<=&&dp[cp+].Y>) break;
zp=dp[cp].X-dp[cp].Y*(dp[cp+].X-dp[cp].X)/(dp[cp+].Y-dp[cp].Y);
}
res[i]=min(zp,q);
} for(int i=n-;i;i--)
if(res[i]+a>res[i+]) res[i]=res[i+]-a;
else if(res[i]+b<res[i+]) res[i]=res[i+]-b;
for(int i=;i<=n;i++)
printf("%.6lf ",res[i]),sum+=(res[i]-dat[i])*(res[i]-dat[i]);
printf("\n%.6lf",sum);
return ;
}

Problem E

对于$dp$模型和值域相关的题目可以考虑这样的函数式优化

如果能证明函数为单峰函数就非常方便了

同时有时候在值域很大时求单峰函数最值不能直接用三分法,会TLE(如此题)

这时就要从导数的角度考虑,看看导数有没有什么性质能不需要二分(此题导数为分段形式,记录端点来优化)

可以考虑将每个$dp[i]$和$dp[i]'$的函数图像画出来方便猜结论

[Codeforces #172] Tutorial的更多相关文章

  1. [Codeforces #514] Tutorial

    Link: Codeforces #514 传送门 很简单的一场比赛打崩了也是菜得令人无话可说…… D: 一眼二分,发现对于固定的半径和点,能包含该点的圆的圆心一定在一个区间内,求出区间判断即可 此题 ...

  2. [Codeforces #210] Tutorial

    Link: Codeforces #210 传送门 A: 贪心,对每个值都取最大值,不会有其他解使答案变优 #include <bits/stdc++.h> using namespace ...

  3. [Codeforces #196] Tutorial

    Link: Codeforces #196 传送门 A: 枚举 #include <bits/stdc++.h> using namespace std; #define X first ...

  4. [Codeforces #174] Tutorial

    Link: Codeforces #174 传送门 A: 求原根的个数,有一条性质是原根个数为$\phi(\phi(n))$,多了一个不会证的性质 如果要确定哪些是原根的话还是要枚举,不过对于每个数不 ...

  5. [Codeforces #190] Tutorial

    Link: Codeforces #190 传送门 A: 明显答案为$n+m-1$且能构造出来 #include <bits/stdc++.h> using namespace std; ...

  6. [Codeforces #211] Tutorial

    Link: Codeforces #211 传送门 一套非常简单的题目,但很多细节都是错了一次才能发现啊…… 还是不能养成OJ依赖症,交之前先多想想corner case!!! A: 模拟,要特判0啊 ...

  7. [Codeforces #192] Tutorial

    Link: Codeforces #192 传送门 前两天由于食物中毒现在还要每天挂一天的水 只好晚上回来随便找套题做做找找感觉了o(╯□╰)o A: 看到直接大力模拟了 但有一个更简便的方法,复杂度 ...

  8. [Codeforces #201] Tutorial

    Link: 传送门 代码量很少的一套思维题 A: 试一试发现最后状态一定是所有$min,max$间$gcd$的倍数 直接判断数量的奇偶性即可 #include <bits/stdc++.h> ...

  9. [Codeforces #188] Tutorial

    Link: Codeoforces #188 传送门 A: 先全转为正数,后面就全是指数级增长了 #include <bits/stdc++.h> using namespace std; ...

随机推荐

  1. solr笔记之安装部署到tomcat

    1. 下载 solr 去官网下载,下载的时候选清华的镜像源,这个页面:https://mirrors.tuna.tsinghua.edu.cn/apache/lucene/solr/7.1.0/ 在/ ...

  2. Django(基础篇)

    1.请求周期 url> 路由 > 函数或类 > 返回字符串或者模板语言? Form表单提交:        提交 -> url > 函数或类中的方法           ...

  3. ecshop代码修改后提交,无法立即生效

    今天帮一朋友部署一网站.成品的ecshop模版站.在搭建好xammp集成环境,导入数据库,修改配置文件后,报了一大堆错. 其中第一个是关于废弃preg_replace中/e这种用法的,因为存在漏洞,一 ...

  4. 项目开发 -- ZFS容量分配

    存储池 allocated 池中已实际分配的存储空间量.该属性也可通过其简短列名alloc来引用. capacity 已用的池空间百分比.此属性也可通过其简短列名cap来引用. dedupratio ...

  5. Coursera在线学习---第三节.归一化处理(Normalize)

    一.归一化(也说标准化)作用 1)将有量纲特征转化为无量纲特征 2)能够加快收敛(主要指梯度下降法时) 二.Octave中计算          mean(A)   求解矩阵中每一列的均值 std(A ...

  6. Linux下libevent安装与示例

    http://www.cnblogs.com/kunhu/p/3632225.html

  7. UOJ#58/BZOJ 3052【WC2013】糖果公园

    好写好调的莫队算法,就算上树了仍然好写好调. 传送门 http://uoj.ac/problem/58 简要做法 将树按照dfs序分块,然后将询问按照(u所在块,v所在块,时间)作为关键字进行排序,依 ...

  8. python抓取链家房源信息(三)

    之前写过一个链家网北京二手房的数据抓取,然后本来今天想着要把所有的东西弄完,但是临时有事出去了一趟,耽搁了一下,然后现在是想着把北京的二手房的信息都进行抓取,并且存储在mongodb中, 首先是通过' ...

  9. fatal error LNK1104: 无法打开文件“libc.lib”的问题

    如果将用低版本的VC开发的项目,拿到高版本的VC开发环境上去编译,链接时也许会触发LNK1104错误.解决方案是链接时忽略此库,在此提供三种解决方案: 1.解决如下:项目->属性中->配置 ...

  10. 【转】EventBus 3.0使用详解

    原文:https://www.jianshu.com/p/f9ae5691e1bb 01 前言 当我们进行项目开发的时候,往往是需要应用程序的各组件.组件与后台线程间进行通信,比如在子线程中进行请求数 ...