6的一批(cai de kou jiao)
策略
- 先看题,看懂所有题再深入思考。
- 再难的题也要留出一定时间思考,不要丢了容易拿的分。 看着很难(复杂)的题目一般简单,只是需要简化题意罢了。
- 做比赛的时候可以多猜测。对于一个猜测的结论,最好的方法往往不是证明他,而是验证它。 先尝试举出反例,若找不出再进行对拍。
暴力+猜测+拍拍拍=AC. - 一道题永远不要死磕一种思路。
- 千万不要抱着就拿这题100分就够了的心态,九成会爆炸。拿好大部分人都能拿的分才是第一要做的。
- 拿高分的题一定要拍,即使拍一题会花掉你1h,甚至1.5h时间,但不拍的后果通常都是拿不到预期的分数(fst)
- 每一道题都要留出至少20分钟的思考时间,不管是难题还是简单题还是题面玄学的题还是涉及到各种没听过知识点的题。 不要丢了容易拿的暴力分或容易拿的高分。
一般来说看起来很难如果能放到你所做的比赛,有两种可能,一是他真的是压轴题,二就是只是用题面吓你的简单题。 - 思考时要时刻紧扣题目所求,不要神游!不要神游!不要神游!
- 一般图的题不能只对拍,因为很多情况都难随机到,要手出数据,所有题目没明说不可能的情况都要试一下,尽量兼容。
- 遇到一套难题千万不要慌,把可能拿到的部分分列一下,把各题暴力打打,也许会有意想不到的收获。一直想题不一定能想出来,而且时间一长容易紧张。
- 想完算法复述一下题面,看看算法靠不靠谱,有没有漏掉的关键细节。
实现
无向边集数组应该开两倍!!!
std有些东西在不开O2的时候常数炒鸡大,开O2的时候就比自己写的都要快了。
永远不要把除法(或mod)放在频繁使用的地方,比如循环条件.
如果题目给出了精度要求,那么最好多开2到3位.
(int)x.9999999999999999999(实际上等于x+1的一个小数)=x,所以如果要取某个小数的下一个整数的话 ,那么就需要判断他本身就是一个整数的情况
二分的ans有可能是没有的,注意判断合法性
splay等数据结构能不写成struct就不写成struct,不然写起来很别扭。
splay的down可以在kth的时候完成,这样就不需要再写一个 down了。 (如果被splay的点一定是被kth出来的话)
在c++中x0modk (x0<=0)等价于x0不断加k直到正好小于等于 0.
实现lct的splay需要注意的一些地方:
void rotate(int x) {
int y=fa[x],z=get(x);
if (c[x][1-z]) fa[c[x][1-z]]=y;
c[y][z]=c[x][1-z];
fa[x]=fa[y];
if (!isroot(y)/*这里容易漏掉,并不是简单判断fa为0即可*/) c[fa[y]][get(y)]=x;
fa[y]=x;
c[x][1-z]=y;
update(y);
}
void downs(int x) {
while (!isroot(x)) Q[++Q[0]]=x,x=fa[x];
Q[++Q[0]]=x; //要补加一个
while (Q[0]) down(Q[Q[0]--]);
}
- 分块些要注意的地方
ll nn,mm;
ll calc(ll a,ll x) {
a=min(a,x);
ll ret=0,l=1,r;
while (l<=a) {
r=min(x/(x/l),a); //若a!=x,这里必须要取min
ret=(ret+(x/l)%mo*sum(l,r)%mo)%mo;
l=r+1;
}
return ret;
}
- long long 相乘模long long,用加法代替乘法,时间换正确性。
ll mult( ll A, ll B, ll Mo ) {
ll ret=0;
while (B) {
if ((B&1)>0) ret=(ret+A)%Mo;
A=(A+A)%Mo; B=B>>1;
}
return ret;
}
zz操作: 如果记得判当前剩余数是个质数的话(当前质数大于
剩余的数−−−−−−√ ),分解质因数的时间是稳定小于x√ 的!数据结构区间tag无论是修改还是查询,只要需要update就要down,不然update就会把原tag的修改覆盖掉。
(dij)迪杰特斯拉求最短路若原图不连通则算法停止条件不是i< n,而是堆中点数是否为0.
注意爆栈问题,保守起见尽量不要有超过1w层的递归 (jzoj上好像15w才会爆?而且测试测不出来!?)。
遇到头疼的数位DP时,有两种解决办法:
1.直接枚举两个相关状态,并将所有不合法转移判掉.
2.枚举之前的状态与当前数,生成当前状态.struct重载运算符的姿势
struct line {
ll l,r,no;
friend bool operator<(line x,line y){
return x.r<y.r || x.r==y.r && x.l>y.l;
}
line(ll l,ll r,ll no)
{this->l=l, this->r=r, this->no=no;}
line() {l=r=no=0;}
} a[N],b[N];
//高精度数直接取的姿势,**不开O2慎用!**
struct _int {
ll a[Mx];
ll * operator[](ll i) {return a[i];}
} n;
struct mat{
int h[100][100];
int * operator[](int x) {return h[x];}
} a;
线段树上第k小只需要一个log,不需要二分!!!!
double
4 byte15位有效数字 max = 1e308
long double
8 byte 18位有效数字 max = 1e4000c++自带log的常数很大!可以预处理数组!
快速幂尽量写非递归版的
ll pow(ll x,ll y){
ll t;
for(t=1; y; y>>=1,x=x*x%mo) if(y&1) t=t*x%mo;
return t;
}
- 用define打的min千万不要放时间长的函数进去!
错误示范
return min( query(x*2,l,mid,tl,tr),query(x*2+1,mid+1,r,tl,tr));
正确姿势
int fm1=query(x*2,l,mid,tl,tr),fm2=query(x*2+1,mid+1,r,tl,tr);
return min( fm1,fm2 );
- 常用的编译参数
devcpp用法:工具-编译选项-编译时加入以下命令
-Wl,--stack=167772160 开栈
-o2 你懂得
两个加起来并排打,也就是(不含引号)
"-Wl,--stack=167772160,-o2"
函数,变量名不要用hash,next,y0,x0,time等可能引起编译错误的变量。
如果ans是个ll,n,q是int,那么
ans+(ll)(n-1)*Q 不会爆炸
ans+(n-1)*Q 会
因为当int与ll运算时,int才会自动转ll,而这个是(n-1)先与Q运算。如何生成n个互异随机数:< algotihm > random_shuffle打乱数组
思路
lca的查询复杂度是O(1)的 _______by RMQ&ST
只带路径压缩或者带秩合并的并查集的时间复杂度其实都是均摊logn的 (但是一般卡不到),一起带才是nα(n) by 栋爷《数据结构小入门》
反演容斥是一家
CDQ分治与整体二分的异同
cdq分治=按(操作)时间分治。先处理前半部分修改对后半部分的影响,再两边分别分治下去。
整体二分=同时二分所有询问的答案,将可能区间相同的询问放到一起。 枚举所有操作,先处理好每一询问判定需要的信息,再判定每一询问该被分到哪一部分。
比如说二分第k大数为mid,先将原序列排序变为用来二分的答案序列,当前可能区间为[l,r]。 标记所有大于mid=(l+r)/2的位置,查询区间[l,r]中有多少大于mid.从而判定答案大于mid还是小于等于mid.
记得统计在询问x之前但被分到另一区间的y操作的贡献。
http://blog.csdn.net/jokerwyt/article/details/78482831三角形面积可以转化为叉积(
ax∗by−bx∗ay2 ),线段长度可以转化为点积(axbx+ayby=长度的平方).枚举
x 与d|x 所用的时间可以做到O(nlgn) 树分治大多数时候是容斥(lca不是当前分治中心的情况要减去之类的)类似当前算一遍答案,然后减去子树答案.
数学题如果碰到带下取整的,考虑分块。
如果是函数,则分块自变量。
比如说我们现在要分块的是F(x)这个式子。
设F(x)>=trunc(F(l)),x>=l.
解出来所得到的x取值范围就是相同的个数。若存在
ax+by=c ,c必然有因数(a,b),(a,y),(x,b),(x,y) 。将原方程看作以选定两个数为系数的不定方程可得。
反过来,当c没有因数(a,y) 时,必定不存在x,b满足ax+by=c 最小割经典约束:当前决策x,某个决策y就必须满足某些条件f(x,y)
【Srm590】Fox And City(fox) 3749
【HNOI2013】切糕 3222莫比乌斯反演的两种常见套路:
要求的函数为g。
1.f为其所有约数的g函数之和。⇒ g(x)为约数的f*mui之和。 (容斥范围:x的约数)
2.f为n以内其所有倍数的g函数之和。⇒ g(x)为n以内所有倍数的f*mui之和(容斥范围:x的倍数)(n/d)/i=n/(i*d),整除。 证明略去。所以分块n/(id)等价于分块(n/d)/i
欧拉函数和莫比乌斯函数的两个性质 (狄利克雷卷积)
-
n=∑d|nφ(d)
当n=pk 时上式成立,又因为f(n)是积性函数(莫比乌斯反演可推),得证! -
∑d|nμ(d)=[n==1]
使得μ(d) 不为0则d不能有平方因子,二项式定理可证。
-
O(n) 求阶乘逆元,有逆元的充要条件是(p,x)=1(1<=x<=n)
设f(x)=x!−1(modp) ,
f(x)=x∗1(x+1)!=x∗f(x+1) O(n)求1..n关于p的逆元,前提是1..n与p互质。
x∗(m/x)+(m mod x)=0(modm)
x=−(mmodx)m/x(modm)
x−1=−(m/x)∗(m mod x)−1 卢卡斯定理求
Cmn,
设ni,mi满足n=∑ki=0ni⋅pi,m=∑ki=0mi⋅pi ,也就是表示n,m在p进制下的第i位数字
C(n,m)≡∏ki=0C(ni,mi)(modp) ,递归形式是
Cmn=Cm/pn/p ∗ Cm mod pn mod p (整除)
注意卢卡斯定理的局限性:它只适用于模质数的情况。其他方法- 对于不平凡的参数求组合数先考虑约分,比如求C(n+i,i)
- 多重背包有两个办法,一是按位分物品(1,2,4,8…+一个数可实现所有取法覆盖),一是单调队列。前者比后找多一个log 总结原文
- 次小生成树的
O(m log n) 方法:选上一条没选的边,减去mst上的瓶颈路。
http://blog.csdn.net/sluqy671/article/details/41720785 - 有、无向图tarjan缩环(边双连通分量)
http://blog.csdn.net/jokerwyt/article/details/52516186 - 如何判断树上路径相交?+ 树状数组+dfs序维护各种信息 jzoj5290行程的交集
- 给定二叉树的前序遍历与后序遍历,若每个点都有两个儿子或没儿子,那么只有唯一一种构造方法。 (否则x个点有1个儿子就有2^x种方案,k叉树可以用C函数类似推广)
自然数幂和的几种求法:
O(k^2)递推式 http://blog.csdn.net/jokerwyt/article/details/54141757
拉格朗日插值法 http://blog.csdn.net/jokerwyt/article/details/78165667卡特兰数的通项公式
http://blog.csdn.net/jokerwyt/article/details/77414853哈夫曼树的O(n)构造方法 (nlogn预处理)(合并果子的O(n)方法) http://blog.csdn.net/angon823/article/details/52598438
证明:对于节点a< b< c,先选a,b则权值a+b+a+b+c,若选a,c则a+c+a+c+b,类似推广。
不能取完的,先取掉多余的。若一开始不取,考虑构造出来的哈夫曼树,总可以将深度大的节点挪到深度小的儿子上, 这样会更优。∑ni=1[(i,n)=1]i
=φ(i)∗i/2 (当i不等于1时,否则=1)
若包括1的情况可以加上0.5来补足。- 【51Nod1833】环 有向图的每个简单环覆盖对应着二分图(原有边i,j拆为i,j’)中的一组完美匹配. (任意点保证一条入边,一条出边)
- 曼哈顿距离(斜着的正方形)转正方形,坐标轴顺时针旋转45”,并放大
2√ 倍(x,y)->(x-y,x+y)。关注原坐标轴上的点可得。 - 为了减少记忆量,坐标旋转公式可用复数乘法轻易推得。 (复数乘法性质: 两复数相乘,其结果模长相乘,极角相加。)
- 带绝对值的式子,考虑用数据结构分符号分类求解! (如数据结构+cdq分治等)
- 多边形的三角剖分的一个结论:必定存在一条将两边都分为>=1/3的对角线
模板
heap:
#include<queue>
priority_queue<T,vector<T>,greater<T> > q; 小根
priority_queue<T> q; 默认大根,切记切记
少用operator<,容易翻车
//manachar
//先在开头结尾和字符中间插入#
//r[i]为回文串向右延伸长度,比如ababa,r[3]=3.
void Build() {
int p = 0, mx = 0;
fo(i, 1, n) {
r[i] = mx > i ? min(r[p * 2 - i], mx - i + 1) : 1;
while (i > r[i] && s[i - r[i]] == s[i + r[i]]) r[i] ++;
if(i + r[i] - 1 > mx) p = i, mx = i + r[i] - 1;
}
}
//堆
void swap(int &x,int &y) {
int z=x; x=y; y=z;
}
void down(int x) {
if(x*2>tt) return;
int y=x*2; if(x*2<tt && d[y+1]<d[y]) y++;
if(d[x]>d[y]) swap(d[x],d[y]),down(y);
}
void up(int x) {
if(x==1) return;
int y=x/2;
if(d[x]<d[y]) swap(d[x],d[y]),up(y);
}
splay模板题
3599. 【CQOI2014】排序机械臂
#include <cstdio>
#include <iostream>
#include <algorithm>
#define maxn 100100
#define fs first
#define sc second
#define isRc(x) ((c[fa[x]][1])==(x))
using namespace std;
typedef pair<int,int> mp;
int n,a[maxn],rmd[maxn],sum[maxn],c[maxn][2],lazy[maxn],root;
int fa[maxn];
mp tmp[maxn];
int build(int l,int r) {
if (l>r) return 0;
int x=l+r>>1;
rmd[x]=1;
sum[x]=r-l+1;
if (l!=r) {
c[x][0]=build(l,x-1);
c[x][1]=build(x+1,r);
fa[c[x][0]]=fa[c[x][1]]=x;
}
return x;
}
void update(int x) {
sum[x]=sum[c[x][0]]+sum[c[x][1]]+rmd[x];
}
void putTag(int x) {
lazy[x]^=1; swap(c[x][0],c[x][1]);
}
void downTag(int x) {
if (lazy[x]==1) {
lazy[x]=0;
putTag(c[x][0]); putTag(c[x][1]);
}
}
int Q[maxn];
void downTags(int x,int y) {
while (fa[x]!=y) Q[++Q[0]]=x,x=fa[x];
while (Q[0]) downTag(Q[Q[0]--]);
}
void rotate(int x) {
int y=fa[x],z=isRc(x);
//Step 1. do y,c[x]
c[y][z]=c[x][1-z];
if (c[x][1-z]!=0) fa[c[x][1-z]]=y;
//Step 2. do fa[y],x
fa[x]=fa[y];
if (fa[y]!=0) c[fa[y]][isRc(y)]=x;
//Step 3. do x,y
c[x][1-z]=y,fa[y]=x;
update(y); //Final update y
}
void splay(int x,int y){ //必须按照如下方法旋转,否则无法保证势能
downTags(x,y);
if (y==0) root=x;
while (fa[x]!=y) {
if (fa[fa[x]]!=y)
if (isRc(x)==isRc(fa[x])) rotate(fa[x]); else rotate(x);
rotate(x);
}
update(x);
}
void printTree(int x) {
if (x==0) return;
downTag(x);
printTree(c[x][0]); if (rmd[x])cout<<x<<" "; printTree(c[x][1]);
}
int main() {
freopen("3599.in","r",stdin);
//freopen("3599.out","w",stdout);
scanf("%d",&n);
for (int i=1; i<=n; i++) scanf("%d",&a[i]),tmp[i]=make_pair(a[i],i);
root=build(1,n);
sort(tmp+1,tmp+1+n);
int loc;
for (int i=1; i<=n; i++) {
loc=tmp[i].sc;
splay(loc,0);
printf("%d ",sum[c[loc][0]]+i);
rmd[loc]=0;
putTag(c[loc][0]);
update(loc);
}
}
3766【BJOI2014】大融合
lct虚边维护子树大小
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100100
#define isroot(x) (c[fa[x]][0]!=(x) && c[fa[x]][1]!=(x))
#define get(x) ((c[fa[x]][1]==(x)))
typedef long long ll;
using namespace std;
ll c[N][2],fa[N],rev[N],add[N],size[N],fsum[N],ff[N];
ll n,q;
int Q[N];
void flip(int x) {
if (x!=0) rev[x]^=1,swap(c[x][0],c[x][1]);
}
void down(int x) {if (rev[x]) flip(c[x][0]),flip(c[x][1]),rev[x]=0;}
void downs(int x) {
while (!isroot(x)) Q[++Q[0]]=x,x=fa[x];
Q[++Q[0]]=x;
while (Q[0]) down(Q[Q[0]--]);
}
void update(int x) {
size[x]=size[c[x][0]]+size[c[x][1]]+1;
fsum[x]=fsum[c[x][0]]+fsum[c[x][1]]+ff[x];
}
void rotate(int x) {
int y=fa[x],z=get(x);
if (c[x][1-z]) fa[c[x][1-z]]=y;
c[y][z]=c[x][1-z];
fa[x]=fa[y];
if (!isroot(y) && fa[y]) c[fa[y]][get(y)]=x;
fa[y]=x;
c[x][1-z]=y;
update(y);
}
void clear(int x) {
if (x==0) return;
down(x);
clear(c[x][0]),clear(c[x][1]);
}
void splay(int x) {
downs(x);
while (!isroot(x)) {
if (!isroot(fa[x])) {
if (get(x)==get(fa[x])) rotate(fa[x]); else rotate(x);
}
rotate(x);
}
update(x);
}
void access(int x) {
for (int i=0; x!=0; i=x,x=fa[x]) {
splay(x);
ff[x]+=fsum[c[x][1]]+size[c[x][1]]-fsum[i]-size[i];
c[x][1]=i;
update(x);
}
}
void makeroot(int x) {
access(x);
splay(x);
flip(x);
}
void link(int x,int y) {
makeroot(x);
access(y);
splay(y);
ff[y]+=fsum[x]+size[x];
fsum[y]+=fsum[x]+size[x];
fa[x]=y;
}
int main() {
//freopen("2.in","r",stdin);
//freopen("2.out","w",stdout);
cin>>n>>q;
for (int i=1; i<=n; i++) size[i]=1;
for (int i=1; i<=q; i++) {
char cc; int u,v;
scanf("\n%c %d %d",&cc,&u,&v);
if (cc=='A') link(u,v);
if (cc=='Q') {
makeroot(u);
ll sizea=fsum[u]+size[u];
access(v);
splay(v);
ll sizeu=ff[u]+1;
printf("%lld\n",(sizea-sizeu)*sizeu);
}
}