1.定义一下自己习惯的变量名。
比如我比较喜欢在离散化的时候用lsh这个变量存离散值。
b[++ lsh] = val[i];
sort(b+1,b+1+lsh);
Rep(i,n)val[i] = lower_bound(b + 1,b + 1 + lsh,val[i]) - val;
2.定义一些比较常用的东西
如树套树时候的一些namespace什么的。
3.记住一些常用的东西?
//总结板子很重要?
sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + f[i][j];
4.在算方案数的时候,如果自信递推+组合数一类东西不会挂掉,那么就用,但是要用DP对拍。
如果实在不知道哪个错了,那么请默认组合数挂掉了。
5.环套树用一遍for进行实现,通常树边不影响答案,那么实际上我们计算一下有多少是环上的边就行了,最后再统计树边的贡献。
膜拜一下fsf的代码:
# include <cstdio>
# define REP(i, n) for (int i = 1; i <= n; ++ i)
# define FOR(i, a, b) for (int i = a; i <= b; ++ i)
# define NR 1001000
const int mod = 1000000007;
int n, K, p[NR], deg[NR];
int f[NR][2];
int vis[NR];
bool ring[NR];
int main () {
freopen("paleta9.in","r",stdin);
scanf ("%d%d", &n, &K);
REP (i, n) scanf ("%d", &p[i]), ++ deg[p[i]];
f[1][1] = K;
FOR (i, 2, n)
{
f[i][0] = (1ll * f[i - 1][0] * (K - 2) + 1ll * f[i - 1][1] * (K - 1)) % mod;
f[i][1] = f[i - 1][0];
}
int ans = 1;
REP (i, n) if (vis[i] == 0) {
int _i;
for (_i = i; vis[_i] == 0; _i = p[_i]) vis[_i] = i;
if (vis[_i] != i) continue;
int len = 0;
for (; !ring[_i]; ++ len, _i = p[_i]) ring[_i] = true;
//printf ("!%d\n", len);
ans = 1ll * ans * (len == 1 ? K : f[len][0]) % mod;
}
REP (i, n) if (!ring[i]) ans = 1ll * ans * (K - 1) % mod;
printf ("%d\n", ans);
return 0;
}
6.写简单易懂的代码.
if(vis[l][abs(dt)][cur][now])return f[l][abs(dt)][cur][now];
int &st = f[l][abs(dt)][cur][now];
vis[l][abs(dt)][cur][now] = 1;
这是某天zxn写codeforces调一个大水题没调出来的时候的程序。
你就不担心哪里打错了?
如果你有一次发现你这个地方打错了一个点
然后你就又要担心你是不是都改了
写短的程序没啥事
写长的程序调的时候需要注意的地方就会塞满你的脑子
把你的脑子留给那些重要的错误上
如果你的脑子一直在被程序细节占据的话你意识到你的做法整个是错的的速度就很慢
“以我的记忆力要随身带个本子(”
本子就像是内存
大脑就是寄存器
“然后有一天zxn想把内存塞到寄存器里面去(”
7.永远要记住板子,哪怕是肌肉记忆,必须要记住板子怎么写。
因为你的脑子还需要用在不需要板子的地方。
8.善用assert来调试程序。
如果你担心assert太慢,那么你可以:
if(A)assert(0);
虽然这样很蠢。
9.注意溢出的情况,我们可以很简单地判定乘法溢出。
显然,如果返回0,就代表乘法溢出了。
if(MAX/A < B)return 0;
return 1;