错题本

posted on 2021-10-03 11:22:24 | under 学术 | source

哇,这个 SB 怎么这么喜欢挂分。

不行,要记录一下是怎么挂分的,防止 CSP-J/S 挂分。<= 年少无知,应该是防止丢掉提高一等

75

全局 lambda 函数不能引用捕获(因为根本没有东西可以捕获)

74

if(*lower_bound(h.begin(),h.end(),fa[x])==fa[x]) ans++;

这样可能解引用 h.end()

所以

auto it=lower_bound(h.begin(),h.end(),fa[x]);
if(it!=h.end()&&*it==fa[x]) ans++;

73

请牢记 powershell 的 diff 的用法:

PS C:\Users\user1\Desktop> diff (cat A.out) (cat A.ans)
//文件相同时不会输出任何信息

和 cmd 的 fc 的用法:

C:\Users\user1\Desktop> fc A.out A.ans
正在比较文件 A.out 和 A.ANS
FC: 找不到差异
//文件相同时返回 0,且会有一些奇怪信息

天差地别。

72

部分 OJ 的系统栈不够大,可能给你 dfs 的时候爆炸。

如果不想手写栈遍历树,一种方法是用 bfs 序倒着扫。

71

powershell 的文件输入输出的写法是:

PS C:\Users\user1\Desktop> cat A.in | ./A >A.out

重点是这个 catGet-Content) 的效率比较慢,如果 A.in 有 10 MB 就是读入超时。

解决方法:A.cpp 加入文件输入;使用 cmd。

70

ST 表

点击查看代码
int lg[1<<20];
template<int N,class T> struct STable{
	T f[21][N+10]; int cnt;
	STable():cnt(0){}
	void insert(const T&x){
//		debug("insert({%d,%d})\n",x.first,x.second);
		f[0][++cnt]=x;
		for(int j=1;1<<j<=cnt;j++){
			int i=cnt-(1<<j)+1;
			f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
		}
	}
	T query(int l,int r){
		if(l>r) swap(l,r);
		int k=lg[r-l+1];
		return min(f[k][l],f[k][r-(1<<k)+1]);
	}
};
	lg[0]=-1; for(int i=1;i<=2e5;i++) lg[i]=lg[i>>1]+1;

这样的写法其实不好,因为 lg 不知道用多少,应该这样写:link

69

red(ans+=(popcount(i)?-1:1)*dp(tmp));

容斥系数是 (popcount(i)&1?-1:1) 注意 &1

68

int dit[20],cnt;
int mian(){
  while(R) dit[++cnt]=R%10,R/=10;
  /...
}
void reset(){
  cnt=0;
  memset(dit,0,sizeof dit);//数位 DP 记得清空数组
}

67

vector<int> a(n);
a.push_back(1);//于是你拥有了一个有 n 个零和 1 个一的 vector

66

\((-1)^i\) 应该如何用程序表示呢?(i&1?P-1:1)

65

\(0\) 的逆元是什么?不存在。

如果真的要求 \(0\) 的逆元,大概率不用求,这时候我们化一下式子然后特判。

细节

64

斜率优化怎么处理 \(x\) 相同的情况呢?

auto insert=[&](int i){
  while(L<R&&(getdx(q[R],i)?slope(q[R-1],q[R])>=slope(q[R],i):getdy(q[R],i)>=0)) R--;
  if(getdx(q[R],i)) q[++R]=i;
  else if(getdy(q[R],i)>=0) q[R]=i;
};

细节

63

print(1e18);//ok
print(1e18+1);
print(1e18-1);
print((LL)1e18);//ok
print((LL)1e18+1);//ok
print((LL)1e18-1);//ok

浮点误差害人不浅!没有 ok 的都是错的!

点击查看代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
void print(LL x){printf("%lld\n",x);}
int main(){
//	#ifdef LOCAL
//		freopen("input.in","r",stdin);
//	#endif
	print(1e18);//ok
	print(1e18+1);
	print(1e18-1);
	print((LL)1e18);//ok
	print((LL)1e18+1);//ok
	print((LL)1e18-1);//ok
	print(1e18+1ll);
	print(1e18-1ll);
	return 0;
}

62

BFS 有个 vis 这是常识,它是用来标记已经访问过的状态的。

无论您是在 BFS 树上做什么,都要写 vis,防止一个方案重复进队。即使有效状态数很少。

61

基环树找环边,有个 vis 要标,然而它是找到一条环边就 return,所以为了后面安全,树形 DP 的时候要将所有 vis 都标上。

60

树链剖分:

void cut(int u,int topf){
	top[rnk[dfn[u]=++cnt]=u]=topf;
	if(son[u]) cut(son[u],son[u]);
	for(int i=g.head[u];i;i=g.nxt[i]){
		int v=g[i].v; if(v==fa[u]||v==son[u]) continue;
		cut(v,v);
	} 
}

注意这一句:if(son[u]) cut(son[u],son[u]); 相当于剖了个寂寞。

正确写法:if(son[u]) cut(son[u],topf);

59

取最大值和次大值,用 set/multiset 可能会悲惨挂分。

multiset<int> s;
auto insert=[&](int x){s.insert(x);while(s.size()>2) s.erase(s.begin());};
for(int i=t.head[u];i;i=t.nxt[i]) if(t[i].v!=_) insert(f[t[i].v]+t[i].w);
if(s.size()==1) ans=max(ans,*s.begin());
else if(s.size()==2) ans=max(ans,*s.begin()+*next(s.begin()));
f[u]=*next(s.begin());

反正是各种窒息问题。一种可能的正确的写法是:

int d1=0,d2=0;
auto insert=[&](int x){if(x>=d1) swap(d1,x); if(x>=d2) swap(x,d2);};
for(int i=t.head[u];i;i=t.nxt[i]) if(t[i].v!=_) insert(f[t[i].v]+t[i].w);
f[u]=d1;//d1 最大,d2 次大

58

记得干不出正解就去日暴力。有时候暴力是正解。

57

字典序最小怎么做?

  • 构造合法解,然后搞成最小。
  • 这相当于是后面的数尽可能大,从后往前扫拿最大的。

56

\(pow\neq O(1)\)

55

对于一个组合计数问题,如果发现做不动,不如换个题意,或者将你臆想的题意换掉。不要对着一个东西死磕,OI 中转化思想还是用的很多的。

54

\(nm\leq 10^5\Rightarrow \min(n,m)\leq \sqrt{10^5}\Rightarrow O(nm\min(n,m))=O(N\sqrt{N})\)

53

我们经常见到一类问题:区间修改,区间判断是否全零,或者数零,或者如何如何,云云。

要知道区间数零一般是不可做的(由乃打扑克的子集呢)。还是要依靠特殊性质。

如扫描线是全局数零,就有很聪明的做法。如果保证修改后非负,还能维护最小值和最小值的出现次数。

拓扑排序,可以判一下全局最小值和全局最大值是否都为 \(0\),不过是比较暴力的做法了。

52

喜报:GD 一考生在模拟赛中连续两题没开 <vector> 头文件,编译错误,警钟长鸣 <= 我宣布 #include <vector> 正式加入缺省源
喜报:GD 一考生在模拟赛中在头文件前面 #defind uint unsigned int 导致 100=>0,距今 0 sec,警钟长鸣

注解:Linux 下有个神奇的地方出现了 typedef uint unsigned int,这启示我们不要在头文件前面加宏定义(#define NDEBUG 这种还是可以的)

51

有时候想想,有一些东西的方案是不是唯一的,例如将一棵树分成 \(k\) 个连通块,每块和相等,若点权非负,则方案唯一或没有。

50

判定离一个点曼哈顿距离 \(\leq k\) 的点是否全为黑点,怎么做?

正难则反,对所有白点跑 bfs。那么必须满足 \(dis_{i,j}>k\)

49

auto queryl=[&](int i,int j){int x=i,y=j-k,f=a[x][y]=='#';return ata(x+y,x-k,x)+atb(x-y+m,x,x+k)-f;};
auto queryr=[&](int i,int j){int x=i,y=j+k,f=a[x][y]=='#';return ata(x+y,x,x+k)+atb(x-y+m,x-k,x)-f;};
auto queryu=[&](int i,int j){int x=i-k,y=j,f=a[x][y]=='#';return ata(x+y,x,x+k)+atb(x-y+m,x,x+k)-f;};
auto queryd=[&](int i,int j){int x=i+k,y=j,f=a[x][y]=='#';return ata(x+y,x-k,x)+atb(x-y+m,x-k,x)-f;};

莫队大师不说,访问可能越界的 a[x][y]

48

graph

graph<100010,100010> g;
g[1]=1;

很难想象是怎么过编译的:将第一条边修改为 \((1\to 0)\)

47

微不足道的:你在写 C++,lambda 表达式写完记得分号。

46

使用 basic_string<> 时记得加 <string> 头文件!

另:vector<int> a 的头插方法是 a.insert(a.begin(),val),头删是 a.erase(a.begin()),尾插是 a.push_back(val),尾删是 a.pop_back()

Test.cpp
#include <cstdio>
#include <vector> 
using namespace std;
vector<int> a={1,1,4,5,1,4};
int main(){
	auto print=[&](){for(int x:a) printf("%d ",x);puts("");};
	a.push_back(114514),print();
	a.insert(a.begin(),1919810),print();
	a.pop_back(),print();
	a.erase(a.begin()),print();
	return 0;
}

45

优化暴力时,一定要存档,不要 ctrl+z,不要 ctrl+z,避免忘了暴力怎么挂的

另:path 是 cmd 命令

44

std::deque 可以当作单次操作线性的数据结构,不要用。

0/1 bfs 时显然可以两个队列直接做:

queue<int> q[2]; 
void bfs(int s){
	memset(dis,0x3f,sizeof dis);
	for(q[0].push(s),dis[s]=0;!q[0].empty()||!q[1].empty();){
		int k=q[0].empty()||(!q[1].empty()&&dis[q[0].front()]>dis[q[1].front()]);
		int u=q[k].front(); q[k].pop();
		for(int i=g.head[u];i;i=g.nxt[i]){
			int v=g[i].v;
			if(dis[v]>dis[u]+g[i].w){
				dis[v]=dis[u]+g[i].w;
				if(g[i].w) q[1].push(v);
				else q[0].push(v);
			}
		}
	}
}

43

for(scanf("%*d");scanf("%s",buf);reset(),mian());

正确写法分别为:while(~scanf("%d",&x))while(cin>>x)

42

对于两棵不同子树的问题,如果用到同一个指针 / 线段树,那么做完后分别清空或者分开两个变量。

不要直接继承。不要两棵线段树(动态开点存根)同时清空。

41

bool check(int j,const char*a){
	int m=strlen(a+1);
//                   ^~~
	for(int i=0;i<m;i++){
		if(s[i+j]!='?'&&s[i+j]!=a[i]) return 0;
	}
	return 1;
}
if(check(i+1,"sakana"))

下标人属于是

40

近日有出题人使用模数 \(998244{\color{red}8}53\) 导致 100->10 距今 0 天警钟长鸣

近日有出题人使用模数 \(99{\color{red}3}244353\) 导致 ?->? 距今 0 天警钟长鸣

39

喜报:近日广东一考生竟反向容斥(斥容),WA on test 9,距今一天,警钟长鸣

for(int i=m;i>=1;i--){
	for(LL j=1;j*j<=i;j++){
		if(i%j==0){
			if(i!=j) l[i]=(l[i]-l[j]+P)%P;
			if(i/j!=j&&j!=1) l[i]=(l[i]-l[i/j]+P)%P; 
		}
	}
} 

注解:\(l_i={\tt constant}-\sum_{j|i,j<i}l_j\) 的容斥,正确写法为:

for(int j=1;j<=m;j++){
	for(int i=j+j;i<=m;i+=j) l[i]=(l[i]-l[j]+P)%P;
}
for(int i=1;i<=m;i++){
	for(int j=1;j*j<=i;j++){
		if(i%j==0){
			if(i!=j) l[i]=(l[i]-l[j]+P)%P;
			if(i/j!=j&&j!=1) l[i]=(l[i]-l[i/j]+P)%P; 
		}
	}
} 

38

喜报:近日……乘法不开 LL,……警钟长鸣

for(int i=1;i<=m;i++) f[i]=qpow(i*(i+1)/2,n);
//m<=1e5

37

喜报:近日广东一考生试图用 scanf("%1d") 输入 \(4\times 10^6\) 个 0/1 数字,卡成暴力,距今 1min,警钟长鸣

注:被卡常了

36

喜报:近日广东一考生在模拟赛中使用如下代码,整题 RTE,距今 1h,警钟长鸣

LL f[510][200010];
//注:空间 700 MB

喜报:近日广东一考生在 4h 的模拟赛中冲了 2h 的 D,最终直接爆零,距今 2h,警钟长鸣

35

SS @NobodyThere 提高 T2 编译错误 警钟长鸣

#define positive [](int x){return x>0;}

void init(bool lim(int x)){
	puts("compile error");
}
init(positive);

#include <functional>
void init(std::function<bool(int)> lim){
	puts("Accepted");
}
init(positive);

考场 Dev-c++ 能过编译。

34

LL dp(){
    for(int i=1;i<=n;i++) f[i]=1e18;
    for(int i=n+1;i<=n*2;i++) f[i]=-1e18;
    for(int i=1;i<=n;i++) if(!deg[i]) q.push({f[i],i});
    for(int i=n+1;i<=n*2;i++) if(!deg[i]) f[i]=0,expand(i);
	//...
}

\(f_i\) 没有初始化!

有的时候发现写出来的代码没过样例,一种可能是写挂了(代码问题),一种可能是你假掉了(你的问题),一般是前者呢

33

LL solve2(int l,int r){
//	f[l-1]=0;
//	LL res=0;
//	for(int i=l;i<=r;i++){
//		f[i]=(a[i]*(f[i-1]+1)+(1-a[i])*f[i-1]%P*T)%P;
//		//f[i]=a[i]*f[i-1]+a[i]-a[i]*f[i-1]*T+f[i-1]*T
//		//    =f[i-1]*(a[i]-a[i]*T+T)+a[i]
//		res=(res+(f[i-1]+1)*a[i])%P;
//	}
//	return res;
	return ((f0*ccf.query(l,r))[0][1]+t.query(r)-t.query(l-1))%P;
}

看到那个 \(1-a[i]\) 了吗?减爆了,没取模,样例过不了,而开始怀疑算法正确性。

32

void dp(){
	f[0][0][0]=1;
	for(int i=1;i<=n*2;i++){
		for(int j=0;j<=n;j++){
			for(int k=0;k<=n*(n-1)/2;k++){
				f[i&1][j][k]=((f[(i^1)&1][j-1][k]+g[(i^1)&1][j+1][k])%P-g[(i^1)&1][j+1][k-j-1]+P)%P;
//				printf("f[%d][%d][%d]=%d\n",i,j,k,f[i][j][k]);
			}
			g[i&1][j][0]=f[i&1][j][0];
			for(int k=1;k<=n*(n-1)/2;k++) g[i&1][j][k]=(g[i&1][j][k-1]+f[i&1][j][k])%P;
		}
	}
}

\(k-j-1\) 会越界的呢

31

李超线段树,写不写等号都可以,但是

void insert(func<T> f,int p,int l,int r){
		int mid=(l+r)>>1;
		switch((tag[p](l)<=f(l))+(tag[p](r)<=f(r))){
			case 2: tag[p]=f; break;
			case 1: insert(f,p<<1,l,mid),insert(f,p<<1,mid+1,r);break;
		}
	}

注意第二个 insert 传的 p

30

限制放松点,一些量是不会变的

29

复杂度分析:分治,\(<B\)\(O(c^{\color{red}3})\)\(\geq B\)\(O(n)\),那么请问你的复杂度是 \(B=\sqrt{n},O(n^{1.5})\) 吗???

\(\sqrt[3]{\quad}\) 分治???真就严格优于暴力

28

试图在 cmd 中输入 color 以运行 color.exe

正确做法:color.exe.\color

27

直径和半径不要混淆!

26

边分治不能统计单点答案

25

dfs 找环!

void dfs(int u,int fa=0){
	if(vis[u]){
		h[++cnt]=u;
		while(stk[top]!=u) h[++cnt]=stk[top--];
		reverse(h+1,h+cnt+1);
		return ;
	}
	vis[stk[++top]=u]=1;
	for(int i=t.head[u];i;i=t.nxt[i]){
		int v=t[i].v; if(v==fa) continue;
        //            ^~~~~~~~~~~~~~~~~~~
		dfs(v,u);
		if(cnt) return ;
	}
}
void dfs(int u){
	if(vis[u]){
		h[++cnt]=u;
		while(stk[top]!=u) h[++cnt]=stk[top--];
		reverse(h+1,h+cnt+1);
		return ;
	}
	vis[stk[++top]=u]=1;
	for(int i=t.head[u];i;i=t.nxt[i]){
		int v=t[i].v;
		dfs(v);
		if(cnt) return ;
	}
}

有向基环树,从一个点出发保证进环。

哪个是对的捏?是第二个,因为你考虑

2 2
1 2
2 1

显然有一个环 \(1\Leftrightarrow 2\),但是因为 v!=fa 的限制,寄了!

其实为什么不用 toposort 呢?

24

//matrix<N,1,int> r;
template<int N> matrix<N,1> operator+(matrix<N,1> &a,matrix<N,1> &b){
	matrix<N,1> r;
	for(int i=1;i<=n;i++)
		for(int k=1;k<=n;k++)
			r[i][1]=(r[i][1]+1ll*a[(i-k+n)%n+1][1]*b[k][1]%P);
	return r;
}

一个括号,一个保龄,今天你溢出了吗

23

语文作文素材 如果 \(k\) 很小,试试容斥

22

T dinic(int s,int t,T inf=1e9){
	    T flow=0;
	    while(bfs(s,t)) flow+=dinic(s,inf,t);
	    return flow;
	}

递归函数

21

方案数,区间 DP 还是背包?

20

如果只有一个未知数,高斯消元可以不需要

19

期望不能割裂,用 DP

18

廊桥分配:当你发现二分没有单调性时,试试滑动窗口

廊桥分配送我退役!

17

对着 \(k\leq n\leq 10^5\) 想了很久,结果 \(k\leq 50\)

16

题目不需要的东西不要维护,谢谢

FHQ-treap 的合并需要保证相对顺序,就是说,只能合并程序分裂的 treap

15

交互:询问次数 \(2n\) 一上来就询问 \(n\)

请不要使用没用的分治,慎防假 \(\log\)

14

贪心不能做背包,但是能换

13

树上最远点是直径两端点其中之一

12

LL a[100010];

int del=a[i]-a[i-1];

数据没保证啊

11

\(n,m\leq 2000\)

int a[1010][1010]

10

#include <cmath>

/tmp/compiler_2b2px1fj/src: 在函数‘int main()’中:
/tmp/compiler_2b2px1fj/src:50:17: 错误:‘sqrt’在此作用域中尚未声明
   50 |   int k=pri[i]<=sqrt(n)?0:(pri[i]*3>n)+1;
      |                 ^~~~

#include <vector>

/tmp/compiler_tz_45o8w/src:10:1: 错误:‘vector’不是一个类型名
   10 | vector<int> s[1000010];
      | ^~~~~~
/tmp/compiler_tz_45o8w/src: 在函数‘int check()’中:
/tmp/compiler_tz_45o8w/src:15:27: 错误:‘s’在此作用域中尚未声明
   15 |     for(;pos<=n;pos++) if(s[pos].size()<2) break;
      |                           ^
/tmp/compiler_tz_45o8w/src:16:9: 错误:‘s’在此作用域中尚未声明
   16 |     if(!s[pos].size()) pos--;
      |         ^
/tmp/compiler_tz_45o8w/src:17:16: 错误:‘s’在此作用域中尚未声明
   17 |     assertf(1<=s[pos].size()&&s[pos].size()<=2);
      |                ^
/tmp/compiler_tz_45o8w/src: 在函数‘int main()’中:
/tmp/compiler_tz_45o8w/src:29:50: 错误:‘s’在此作用域中尚未声明
   29 |         for(int i=1;i<=n;i++) assertf(dis[i]<=n),s[dis[i]].push_back(i);
      |                                                  ^
/tmp/compiler_tz_45o8w/src:33:32: 错误:‘s’在此作用域中尚未声明
   33 |             printf("%d %d 1\n",s[i][0],s[i+1][0]);
      |                                ^
/tmp/compiler_tz_45o8w/src:38:12: 错误:‘s’在此作用域中尚未声明
   38 |         if(s[pos].size()>1) printf("%d %d 1\n",s[pos][0],s[pos][1]);
      |            ^
/tmp/compiler_tz_45o8w/src:42:40: 警告:格式 ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘LL’ {aka ‘long long int’} [-Wformat=]
   42 |         for(int i=1;i<=n;i++) printf("%d\n",dis[p[i]]);
      |                                       ~^    ~~~~~~~~~
      |                                        |            |
      |                                        int          LL {aka long long int}
      |                                       %lld

缺省源警告

9

线段树特判 \(ql>qr\) 等一系列离谱情况。

8

删调试信息。这种情况,可以在本地编译命令加个 -DLOCAL,然后写个宏定义:

#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...)
#endif

7

LL f(int x){
	return 1ll*x*(x-1)/2;
}
int res=f(114514);

搁着 intLL

6

int d=-114515;
if(d%2==1) printf("odd");
else printf("even");

热知识:-3%2==-1

int mod(int x,int p=P){return (x%p+p)%p;}

5

int maxx(int a,int b){return a>b?b:a;}
//							  ^

\(\max\) 你妈呢

4

template<int N> struct dsu{
    int fa[N+10],size[N+10],cnt;
    dsu():cnt(N){for(int i=1;i<=N;i++) fa[i]=i;}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    bool query(int x,int y){return find(x)==find(y);}
    void merge(int x,int y){
    	int tx=find(x),ty=find(ty);
//							   ^
        if(tx!=ty){
        	fa[ty]=tx;
            size[tx]+=size[ty];
        }
    }
};

自己使用自己

3

int n,m;
graph<100010,300010> g,t;
bool vis[300010];
dsu<100010> s;
int kruskal(){
    int ans=0;
    for(int i=1;i<=g.cnt;i++){
        if(!s.query(g[i].u,g[i].v)){
            vis[i]=1;
            ans+=g[i].w;
            s.merge(g[i].u,g[i].v);
            t.link(g[i].u,g[i].v,g[i].w);
        }
    }
    return ans;
}

请您的 Kruskal 排序

2

void dijkstra(int s,int dis[]){
    priority_queue<node,vector<node>,greater<node> > q;
    memset(dis,0x3f,sizeof dis);
//					^~~~~~~~~~
    memset(vis,0,sizeof vis);
    dis[s]=0,q.push(node(0,s));
    while(!q.empty()){
        int u=q.top().second;q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=g.head[u];i;i=g.nxt[i]){
            int v=g[i].v,w=g[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                q.push(node(dis[v],v));
            }
        }
    }
}

此处 dis 是个指针,int* 的那种,对这个指针取 sizeof,结果是这个指针所占空间大小,而不是这个 SB 想要的 dis 的空间。

两种解决方法:

  1. 在外面 memset(dis,0x3f,sizeof dis),一劳永逸;
  2. memset(dis,0x3f,sizeof(int)*(n+1)),注意是 \(\times (n+1)\)

1

对拍的时候不要重定义两次文件输入输出,慎防 fc 空文件。

解决方法:看一眼。

posted @ 2022-11-07 15:52  caijianhong  阅读(93)  评论(0编辑  收藏  举报