您的位置:首页 > 其它

[置顶] (巨坑)长期经验总结

2016-07-16 20:07 295 查看
1.写dp的时候注意初值和long long
2.在做题的时候不要先想做法,先看看要不要读入优化和long long
3.写之前检查时间和空间,除非是玄学算法否则绝对不能抱有“卡卡常数就过去了”的侥幸心理
4.遇见数据结构题不要着急,先想想有不有离线做法再看看数据范围能不能分块,当然如果是区间问题可以离线+数据范围不大当然用莫队啦
5.写网络流更新d值的时候赋值为点数而不是2147483647
6.如果是long long不要把0x7f和2147483647作为极大值
7.写题的时候不要乱动GDOI防ak题
8.没过样例也可以提交,神级市队爷大麻教你做人
9.看到01分数规划形式的式子一定要想到01分数规划
10.遇到类似“时间”“价值”比询问“大”“小”的或者”长度““比限制小”“超过了限制”的题目而且可以离线一定要想到排序
11.类似比……小/大的条件可以转化为01条件,一般出现这种条件而且是“比……小且比……小”“都比……小”“比……小或比……小”或者某些类似异或的条件“比……小且不比……小或不比……小且比……小”计数或判断的问题的时候可以把这些01条件使用位运算+bitset优化,1/32的常数可以顶一两个log了
12.遇到关于函数的题目,如果是求最……值,直接想能不能三分,证明是否可以三分的话,除非是很简单一眼可以看出或做过的式子,否则放弃数学证明打几个不同的表直接观察是否单峰,这种题目如果发现不能三分一定是有一个公式能直接得出极值
13.碰见“选a必选b,不选a必不选b”的条件可以试着改成逆否命题然后用连边表示约束,如果约束形成经典模型(最大权闭合子图,最大密度子图,二分图最小覆盖,最大匹配,最大独立集),就可以试着网络流,如果没有形成经典模型也要先想一会儿网络流能不能做
14.现在网络流24题的坑还没填完,即使以后填完了也要隔一段时间看一下,这篇文章更要隔一段时间看一下,还要隔一段时间看一下的文章是这个:http://blog.csdn.net/werkeytom_ftd/article/details/50437196,千万别偷懒,比赛前一天别码题,就看网络流24题看这个看栋爷文章和骗分导论,然后如果还有时间纪中几位爷置顶帖都很好可以看看
15.写vector一定要好好计算空间,按两倍算,小心MLE,vector常数极大,上了50w不要写vector写链表
16.类似“任意选一个根”求最大/最小代价的题可以树D,用u[i]表示向上的代价,再用一个f[i]表示把它换为根的代价然后就可以dp了
17.树形dp会T就试着转线性,按后序遍历并处理出上一个兄弟就可以把子树转化为区间,多叉树写不了就写左儿子右兄弟,遇到树上计数或者求最优解的题先考虑树d再考虑点分如果都做不了就弃疗
18.对于树上多条路径“有几条包含这个点”“确定点/边的状态,使所有路径经过的边**和最小”“是不是所有路径都经过这条边”“有没有路径经过这条边”之类的静态题可以考虑树上差分,把路径两端点lca权值-1,lca的父亲权值-1,端点权值+1然后求个size就可以让路径上所有点点权+1,然后如果要改边的话就直接把把路径两端点lca权值-2不减父亲
19.没有想法而且感觉不像数据结构图论等靠模板和码力拿分的题一般都是智商题,可以通过造几组小数据找规律,样例一定要手算
20.tarjan并不能穷举出所有的环,如果环上连了一条边形成的小环会被无视
21.部分分往往是很多的……我们可以先考虑部分分,对想正解也有帮助
22.计数问题,特别是答案要模一个数的答案很大的计数问题,一般都是数学方法递推和dp
23.关于提答题和交互题的总结,由于太多了新开一篇文:http://blog.csdn.net/orzgeotcbrl/article/details/51958604
24.看到棋盘上的题目先黑白染色再说
25.能用欧拉函数做的题不要随便上莫比乌斯反演
26.写lct的时候要注意:
isrt,maintain这种一句话能解决的事也最好检查一下
pushdown的时候记得是^=1而不是=1
rotate的时候要记得不能改变节点0的状态(除了fa以外)
splay的时候要记得把这个节点到这条prefer path的根路径上的点全部pushdown,要从顶向下pushdown所以需要一个栈存这个点到根路径上的点,千万要记得不能漏任何一个点!!!!!!特别是要splay的这个点和根!!!!!!最好的写法如下:
top=0;sta[++top]=x;
for (int y=x;!isrt(y);y=fa[y]) sta[++top]=fa[y];
for (;top>0;top--) pushdown(sta[top]);

要记得改变一个点的儿子之后要把新改变的这个儿子的fa赋为这个点,还要记得maintain(包括access中改变这个点的prefer son,还有cut之前把fa[c[x][0]]和c[x][0]赋为0)
cut可以这样写
void cut(int x)
{
access(x);splay(x);fa[c[x][0]]=0;c[x][0]=0;maintain(x);
}表示把x和x的父亲割裂开
27.有向图=强连通分量+DAG
28.环加外向树=环+外向树
29.写点分治的时候要注意,在这个点上方的点的个数不是n-size[now]而是size[wtf]-size[now]  (wtf设为这棵树暂时的根)
30.map迭代器的写法:for (map<int,int>::iterator it=belong[x].begin();it!=belong[x].end();it++)
其中it->first代表map中这个点的下标,it->second代表map中这个点的值
也就是说比如map[x]=y,那么如果it遍历到这里,那么it->first=x,it->second=y
31.如果要求一个固定形状的三角形或梯形内的点数,且这个三角形/梯形底边和坐标轴平行,可以把这个三角形/梯形内的点数变成在上面的边/腰以下的点数减去另一条不与坐标轴平行的边/腰以下的点数,这样做是一个log的,详见goj222
32.模数是1 << 31的话就可以这样做,先直接用int记录答案,当答案超过(1 << 31)-1时int会自然溢出,最后输出ans & (1 << 31)-1就好
33.写线段树的时候无论什么地方都要写tag[t]+=x,没有地方是写tag[t]=x的,要记得不要一excited就写错
34.有些线段树的题目初值或者tag不为0,这时候就要在build的时候顺手设置初值和tag,要记得不要一excited就忘了
35.对double使用取整函数的时候最好先把被取整数加上一个eps之后再取整
36.dft模板
void dft(cp* a,int pd)
{
for (i=0;i<n;i++) f[i]=a[i];
for (int m=1;m<n;m<<=1){
int p=0;
for (i=0;i<n;i++) if (i % (m << 1)<m){
t[i]=f[p]+w[i % m*((n>>1)/m)]*f[p+n/2];
t[i+m]=f[p]-w[i % m*((n>>1)/m)]*f[p+n/2];
p++;
}
for (i=0;i<n;i++) f[i]=t[i];
}
if (!pd) for (i=0;i<n;i++) f[i].real/=n;
}

由于有太多东西需要脑补所以最好在比赛前一天就全部脑补好记住
37.做字符串题,特别是关于那种有多少个单词的题,特别要注意在跑完了一遍之后可能跑到字符串末尾都忘了统计最后一个单词,所以在跑完一遍之后还要统计一下是不是有一个单词到末尾才结束
比如http://codeforces.com/contest/723/submission/21139416这个程序就忽视了这个问题 http://codeforces.com/contest/723/submission/21139626改成这个程序就可以AC了 38.C++在判断的时候拥有短路特性,也就是如果前面的条件已经可以确定答案就跳过后面的
注意不要被坑到
另:冒爷给出的建议,在进行bool运算时(and,or或者xor)最好写&和│而不是&&和||,因为这样C++不会跳过执行其中的某一项
然后如果要用到一连串bool的值得到答案时,如果一个变量会导致一个函数无法执行就把变量写在前面,否则写在后面
详情看这三个程序http://codeforces.com/contest/723/submission/21153748,http://codeforces.com/contest/723/submission/21166367,http://codeforces.com/contest/723/submission/21166404
39.如果要统计最大值和次大值,在更新最大值之前,一定要先把次大值赋为最大值
40.无奇环=二分图
41.如果有加减运算的时候设置极大值千万不要设为2147483647,不然一进行运算就炸了
42.在sort时用到greater<int>()和less<int>()的时候要调functional这个库
43.我们使用sort函数的时候:

44.我们如果要写hash,可以把模数选为2^k-1,如4194303,这样我们的模操作可以转化为和模数取&,大大减少常数,还不用考虑模之后为负数的情况,好写很多
hash的一种实现方式:
inline bool find(int x)
{
for (int psz=head[x&Mod];psz;psz=nxt[psz]){
if (id[psz]==x) return true;
}return false;
}

/*if (!find(a[i]+a[j]))*/{
id[++tot]=a[i]+a[j];nxt[tot]=head[(a[i]+a[j])&Mod];head[(a[i]+a[j])&Mod]=tot;
}(可以选择加或不加判重,建议加上好)

45.不存在环=树
46.每个质数都存在原根,所以说,如果模数是质数,我们可以把乘法分解成原根的次幂相加
就是说a=yg^aa % m,b=yg^bb % m
a*b % m=yg^(aa+bb) % m=yg^((aa+bb) % m-1) % m
47.类似f[i][j]=sigma(f[i-1][j-b[k]])的dp显然可以FFT优化
48.如果要在树上dfs多次我们可以
#define FOR for (int ii=0;ii<g[now].size();ii++) if (g[now][ii]!=fa[now]) 
显然这个FOR会被重复写很多次,这样子define可以写的快一点
49.新开一个变量一定要赋初值
50.原来cout也资瓷保留x位小数的啊
cout << fixed << (setprecision(2)) << a<< '\n';
这是输出a,保留两位小数
要调iomanip这个库
51.做答案要mod一个数的题,没必要每次做减法都加上一个Mod再取模(当然这样也可以),可以边做边mod,然后最后输出的时候再加上一个Mod再取模(为了保证输出的答案是正数)
52.字符串的题,如果把字符串扩成了原来的两倍或者三倍,一定要记得把数组开大
53.double占位符,读入用%lf,输出用%f,不然可能会出问题,无聊的时候可以顺手加上fclose,防止老爷机
54.十分重要!!!!!
dp或递推的时候,也包括其他用到多层循环的时候,如果是先for(i=1;i<=n;i++)再for(j=1;j<=n;j++)那么就把状态设成f[i][j],如果先for j再for i就设成f[j][i],一定要按照顺序来设状态!这虽然只是一个小小的寻址优化,但是差距确实巨大的,虽然不了解c++寻址的具体过程,但是这个的常数大到使n=2000时n^2算法TLE!zw调了一个晚上才发现这里常数爆了,说明这个是非常非常难调出来的,所以必须要第一次(写的时候)就意识到并且写对!!!!
55.两个 vector swap 是 O(1) 的,就是交换指针,但是应该要写成 a.swap(b)(不确定 swap(a,b) 是什么复杂度)
56.multiset的erase(x)是删除所有值为x的数而不是删一个
57.写杜教筛的时候千万记得把得到的答案记录下来,就是说记忆化,非常重要!和时间复杂度关系很重要!不要用map来记忆化!!!一定要用hash!!!
58.gcd(a,b)=sigma d|gcd(a,b) phi(d)=sigma d|a,d|b phi(d)
59.如果是极限数据随手就可以输入的数学题,一定要输入试试看,输入浪费的时间不到10秒,就当成奉献给长者了;但是养成随手测一测极限数据的好习惯,可以避免许多TLE和RE,不然T了都不知道怎么回事=  =
60.mdzz!==的优先级比^高……不过这个问题Wall编译会提示,所以记得开Wall
61.写杜教筛的时候,千万不要以为自己过了样例对拍了小数据就没什么问题了,你或许过的是假的小数据,因为说不定是你线性筛的功劳,你可以把阈值先调到很小比如1,然后再测样例拍小数据,然后如果能过说明你的杜教筛部分能过,然后记得加个hash记忆化,还有就是这种题的读入一般只有几个数,记得手动输入极限数据,看一下有没有TLE或者RE
62.高维dp的时候如果时间复杂度不太科学可以试着把某一位的枚举ii由ii转移到i改成直接由i-1转移到i,如果可以时间复杂度会稍微优一些
63.点分治的时候一定要记住找到根以后开一个新变量把根记录下来,不然根会随递归而改变(我根一般用全局变量,所以一定要记录
64.记得算时空复杂度看看数组有没有开小
65.写线段树的时候,如果即使是最底层都要调用到他的儿子,那么空间一定记得开8倍,平时没什么事的话线段树空间直接开8倍好了
66.期望题一定要倒着推!期望题一定要倒着推!期望题一定要倒着推!
67.SAM节点数开2倍
68.我真的是……现在才知道阶乘的逆元预处理可以O(n)
fac[0]=1;
for (i=1;i<=m;i++) fac[i]=1LL*fac[i-1]*i%Mod;
ni[m]=qmi(fac[m],Mod-2);
for (i=m;i>=1;i--) ni[i-1]=1LL*ni[i]*i%Mod;
69.浮点数除法伤精度!
70.记得printf("%d\n",(ans+Mod)%Mod)!记得printf("%d\n",(ans+Mod)%Mod);!记得printf("%d\n",(ans+Mod)%Mod);!
71.推/写类欧的时候一定要记得取的m应该是(a+n*b)/c-1,原因自行脑补(大概是求的是[a+bi>=j+1]的和,而不是>=j,所以j只会取到(a+n*b)/c-1,而且j取到(a+n*b)/c就不能保证n+1>(c*j+c-a-1)/b了,那么那个化简式子就是错的)
72.树上的题有一种套路,把树看成dfs序后子树=区间,而且这些区间是互相包含或互不相交的,所以说树是一个括号序列。括号序列如果分治的话,我们可以转化为求子树区间含于[l,r]的所有区间的信息,然后转化为包含mid的所有区间的信息。我们不断把左端点左移,发现这个时候右端点也是不断右移的,然后我们可以暴力插入端点处的信息
73.读入优化不要写错!!!!
inline int rd()
{
 int ret=0;char ch=getchar();
 while (ch<'0' || ch>'9') ch=getchar();
 while (ch>='0' && ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
 return ret;
}
有负数判一下
有把第一个||写成&&的经历
74.写tarjan一类爆搜时我会记一个lst,表示上一个经过的点,然后判断下一个点是不是上一个经过的点来判断有没有走回头路
其实这样不好,以后应该把lst记为上一个经过的边,这样就可以把重边缩成两点环
顺带一提,写成上一个经过的点,错误是TLE而不是WA
75.写lct或者线段树之类的一定要记得,区间加对和的贡献并不是delta,而是delta*size(len),所以一定不要写错
76.代码中写到做实数除法的时候一定要想想除数能不能是0,特别是除以线段的长度什么的
77.子矩阵查询无论如何都可以拆成前缀查询,所以想的时候直接考虑前缀查询即可
78.set非常非常非常慢!!!2000的题,n^2logn,用set维护要跑15s!!!
79.DAG上的题,单源的从dfs树方向考虑,多源的从拓扑排序方向考虑
80.找一个串的最小循环节,用kmp,处理出nxt数组后s[0..i]的最小循环节就是i-nxt[i],如果我们要找的循环节要求满足循环节长度d|len的话,我们看是否满足i-nxt[i]|len,如果不满足,这个串的循环节应该只能是它本身,就是len
81.并查集的题,如果要可持久化,看这个可持久化是回到第x个版本还是查询第x个版本时两个点在不在同一个集合,第二种可以直接用按秩合并的并查集做,只有第一种才需要写可持久化并查集
82.求一堆圆有没有交集的做法是,拿出一个圆,其他的圆在这个圆上会交出一段弧,也就是一个角度区间,然后就看这些区间有没有交即可
83.设n个数和为m,那么其中第k小的数不超过m/(n-k+1)(分析哈夫曼树复杂度的时候我的证法
84.如果发现一个东西可以用数据结构维护,特别是一些数论上的东西要用数据结构维护,先别写,考虑一下修改次数和查询次数分别是多少,如果相差较大可以分块
参考cqoi2017小q的表格
85.无论什么时候写树形dp,dfs的第一句话都是for所有子树递归dfs,特别是dfs里面要用到全局变量的情况,如果for的时候又dfs又dp,那么全局变量在递归的时候会被修改
参考cqoi2017小q的棋盘我WA的写法
86.后缀数组记得如果sa数组从0开始,要先把n以后的rank赋为-1
87.写后缀数组的时候记得每做完一次都要把sum数组清空
88.字符串要求线性的题,一定是kmp系列,因为后缀数组带log,SAM带26
89.勾股数(x,y,z)和满足m>n,(m,n)=1的(dmn,d(m^2-n^2)/2,d(m^2+n^2)/2)一一对应
90.对拍时的小技巧,在pause前输calc,拍出错时会弹出计算器提醒你
91.求某个东西的k次方和可以拆成斯特林数乘阶乘再乘组合数来做,好处是组合数从i推到i+1只和两项有关,省去了转移复杂度
92.一类子树颜色相关的题可能在树上更容易,方法是把同颜色的相邻两个的lca权值变成-1,然后这样刚好能表示是否在子树内出现,修改的话用set维护每种颜色的点改一下和相邻两个的lca的权值即可,如果颜色加一个范围,不修改就是一个主席树,修改就是用树状数组套主席树维护一个带修主席树
93.有原根等价于模数为2,4,p^k,2*p^k
94.写bzoj楼房重建时的一个错误
正确写法:if (mx[t<<1]>x) return a[t]-a[t<<1]+query(ls,x);

错误写法:if (mx[t<<1]>x) return a[t<<1|1]+query(ls,x);
95.n个数的序列,插入一个位置,找前驱后继(比如左边第一个比他小的数之类的)
考虑倒着做,变成删除一个位置找前驱后继,链表实现
这样是O(n)的

96.网络流一定要检查点标号是否从0开始,模板里要用到点数,有时候不是n是n+1
97.询问把某个点换为根的答案先想树形dp
98.看样例!手算样例!
99.矩阵乘法不要写函数,写过程!
100.考前复习FWT
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: