ACM套路集合

1.并查集维护x在数组中且比x大的数 例题:修改数组

2.dfs遍历每个点的所有子树 例题:Bakry and Partitioning

3.优美的枚举 (找到最大值和最小值形成关于i的不等式)例题:Cobb简化枚举

4.用dfs从下到上扫描求各节点到根的距离,然后从上到下扫描判断 E1. Escape The Maze (easy version)

5.多米诺骨牌是1X2的矩阵方格 D1. Domino (easy version)

6.费马小定理:若存在整数 a , p 且gcd(a,p)=1,即二者互为质数,则有a^(p-1)≡ 1(mod p)。

7.树状数组查询区间和,单点修改,区间修改。

8.曼哈顿距离思维题 C. Manhattan Subarrays  D. Nearest Excluded Points

9.整数分解质因子 D. Another Problem About Dividing Numbers

int solve(int x){
	int cnt=0;
	for(int i=2;i*i<=x;i++){
		while(x%i==0){
			cnt++;
			x/=i;
		}
	}
	if(x>1) cnt++;//最后可能是一个质数,故不要忘记+1
	return cnt;
}

注意:long long比int慢两倍,函数中设x为long long可能导致超时;i*i<=x比i<=sqrt(x)快

10.map,set的count函数:返回的是被查找元素的个数。

11.n是奇数,此时n的因子都是奇数,设减去的数为d,那么得到的数为x=n−d,x一定为偶数,而且不为2 的幂次;n是偶数且不是2 的幂次,那么n一定含有某个因子为奇数,那么我们可以将n 减去一个奇数,得到的数一定为奇数;n是2的幂次,首先我们可以将其变成n / 2或者变成一个偶数且不是2的幂次,先看变成一个偶数且不是2的幂次的情况,这样相当于放弃胜利了,把必胜态扔给了对手。那么我们肯定是变成n/2,一直到只剩一个2的时候停止,所以幂次为偶数的时候先手必胜。

(简单博弈问题)D. Deleting Divisors

12.找一个数的因子,O(nlogn)

for(int i=1;i<=n;i++){
	for(int j=i;j<=n;j+=i){
		a[j]++;
	}
}//预处理一个数因子的个数

for(int i=1;i<=N;i++){
	for(int j=1;j<=N/i;j++){
		dp[i*j]+=i;
	}
}//预处理一个数因子总和

13.用背包问题找是否有子序列和等于sum!!!C. Baby Ehab Partitions Again

14.判断两个数质因子是否相同,只需判断两个数所有质因子相乘的结果是否相等。

15.multiset中的erase删除函数,st.erase(1);是删除集合里面全部的1;

16.初始化导致的多样例出错。

17.改变数组成为满足题意的状态时(可多次操作),可想着一个数一个数改变,最后达到效果。

18.(离谱,离谱)数过大时,可用__int128_t,用法示例:

#include<bits/stdc++.h>
using namespace std;
#define sc(x) scanf("%d",&x)
#define sl(x) scanf("%lld",&x)
#define ll __int128_t
#define pb push_back
typedef pair<int,int>PII;
const int Max=1e6+5;
const ll INF=1e15+5;
const ll mod=998244353;
template <typename T> inline void read(T &x) {
	x = 0;
	int f = 1;
	char c = getchar();
	for(; !isdigit(c); c = getchar())if(c == '-')f = -f;
	for(; isdigit(c); c = getchar())x = x * 10 + c - '0';
	x = x * f;
}
ll solve(ll x,ll d,ll len){
	ll e=d*(len-1);
	e+=x;
	ll sum=len*(x+e)/2;sum%=mod;
	return sum;
}
vector<ll>v;
int main(){
	long long n;cin>>n;
	ll k=sqrt((long long)n);
	v.pb(n);
	for(ll i=2;i<=k;i++){
		ll temp=n/i;
		v.pb(temp);
	}
	ll ans=0;
	for(ll i=0;i<v.size();i++){
		if(i+1<v.size()){
			ll len=v[i]-v[i+1];
			if(len>=2){
				ll num=n%(v[i]-1)-n%v[i];
				ll hx=n%v[i];
				ans+=solve(hx,num,len);ans%=mod;
			}
			else ans=ans+n%v[i];ans%=mod;
		}
	}
	for(ll i=1;i<=v[v.size()-1];i++) ans+=n%i,ans%=mod;
	printf("%lld\n",(long long)ans);
}

x在做除法之前,不能取模,不然会改变最后的结果

 19.(真的不用太离谱)离谱题目

#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
ios;
ll n,a,b;
cin>>n>>a>>b;
<--这段代码能过-->

scanf("%lld%lld%lld\n",&n,&a,&b);
<--这段代码段错误-->

20.前缀和可以判断i*j到i*(j+1)-1直接是否有数,如果sum[i*j-1]==sum[i*(j+1)-1]说明不存在,反之存在!!!

21.求解树的重心 树的重心模板题

#include<bits/stdc++.h>
using namespace std;
#define sc(x) scanf("%d",&x)
#define sl(x) scanf("%lld",&x)
#define ll long long
#define pb push_back
typedef pair<int,int>PII;
const int Max=1e7+5;
int sum[Max],total[Max]; 
vector<int>mp[Max];
int n;
void dfs(int fa,int x){
	for(int i=0;i<mp[x].size();i++){
		if(mp[x][i]!=fa){
			dfs(x,mp[x][i]);
			total[x]+=total[mp[x][i]];
			sum[x]=max(sum[x],total[mp[x][i]]);
		}
	}
	sum[x]=max(sum[x],n-total[x]);
	return ;
}
int main(){
	int t;sc(t);
	while(t--){
		sc(n);
		for(int i=1;i<=n;i++) sum[i]=0,mp[i].clear(),total[i]=1;
		int l=0,r=0;
		for(int i=0;i<n-1;i++){
			int u,w;
			sc(u);sc(w);l=u;r=w;
			mp[u].pb(w);
			mp[w].pb(u);
		}
		dfs(0,1);
		int flag=0,word=-1,mina=Max;
		for(int i=1;i<=n;i++){
			if(mina==sum[i]) flag++;
			if(mina>sum[i]){
				mina=sum[i];
				word=i;flag=1;
			}
		}
		if(flag==1) printf("%d %d\n%d %d\n",l,r,l,r);
		else{
			int len=0;
			for(int i=1;i<=n;i++) if(sum[i]==mina&&i!=word) len=i;
			for(int i=0;i<mp[len].size();i++){
				if(mp[len][i]!=word){
					printf("%d %d\n",len,mp[len][i]);
					len=mp[len][i];
					break;
				}
			}
			printf("%d %d\n",word,len);
		}
	}
}

22.先加后,取模,然后减取模,减去后要加上mod再取mod。

23.三维数组跑bfs时,可以定义一维数组表示坐标

int n,m,h;
ll change(int x,int y,int z){
    return x-1+(y-1)*n+(z-1)*n*m;
}

24.预处理阶乘快速求组合数

const int mod=1e9+7;
ll Power(ll base, ll power) {
	ll result = 1;
	while (power > 0) {
		if (power % 2 == 1) {
			result = result * base % mod;
		}
		power = power / 2;
		base = (base * base) % mod;
	}
	return result;
}
ll Finv[Max],fac[Max],inv[Max];
void init(int n)
{
    inv[1]=1;
    for(int i=2;i<=n;++i)inv[i]=((mod-mod/i)*inv[mod%i])%mod;
	fac[0]=Finv[0]=1;
	for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod,Finv[i]=Finv[i-1]*inv[i]%mod;
}
ll C(ll n,ll m)
{
	if(m<0||m>n) return 0;
	return fac[n]*Finv[n-m]%mod*Finv[m]%mod;
}

25.abs()主要用于求整数的绝对值,fabs()主要用于double,float的绝对值。

26.可用单调栈求一个数组中,其中某个数两边第一个大于本身的数,并用二维vector存取,最后记忆化搜索。例题:单调栈+记忆化搜索

int dfs(int x){
	if(vis[x]) return vis[x];
	for(int i=0;i<mp[x].size();i++){
		int v=mp[x][i];
		vis[x]=max(vis[x],dfs(v)+abs(w[x]-w[v]));
	}
	return vis[x];
}

int top=0;
for(int i=1;i<=n;i++){
	int j=i;
	while(top&&h[stk[top]]<=h[i]){
		if(h[stk[top]]==h[i]) j=stk[top];
		--top;
	}
	if(top) mp[i].pb(stk[top]);
	stk[++top]=j;
}
top=0;
for(int i=n;i>=1;i--){
	int j=i;
	while(top&&h[stk[top]]<=h[i]){
		if(h[stk[top]]==h[i]) j=stk[top];
		--top;
	}
	if(top) mp[i].pb(stk[top]);
	stk[++top]=j;
}
for(int i=1;i<=n;i++) cout<<dfs(i)<<" \n"[i==n];

27.求二维数组前缀和的时候,记得赋值sum[i][0]=0,牛客oj不赋值会导致答案错误(初始化一样答案错误)。例题:背包问题+上述坑点

28.尽量避免double与整型比大小,可用乘法代替;输入时注意long long还是int.

29.读取string类型中的数字

string str;
sscanf(str.c_str(),"%d:%d:%d %d:%d:%d (+%d)",&h1,&m1,&s1,&h2,&m2,&s2,&num);

30.少用内置函数,例如sqrt可用二分搜索替代。

31.求逆元板子:

ll extend_gcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;y=0;
		return a;
	}
	ll res=extend_gcd(b,a%b,x,y);
	ll temp=x;x=y;y=temp-a/b*y;
	return res;
}
ll inv(ll a,ll n){
	ll x,y;
	extend_gcd(a,n,x,y);
	x=(x%n+n)%n;
	return x;
}

32.斐波那契数列最大公约数定理:g c d ( f [ n ] , f [ m ] ) = f [ g c d ( n , m ) ] .

33.遇到数学公式就化简,类似背包转换类似背包dp求出来∑ai 所有可能的值.

例题:https://codeforces.com/problemset/problem/1637/D

34. 

35.组合数前缀和

        C(n, n) + C(n + 1, n) + C(n + 2, n) + ... + C(m, n) = C(m + 1, n + 1) 其中m >= n

36.bitset用法

1.定义:
bitset< n > s;
表示一个n位的二进制数,<>中填写位数;

2.位运算操作符:
~s: 返回对s每一位取反后的结果;
&,|,^:返回对两个位数相同的bitset执行按位与、或、异或运算的结果;
<<, >>:返回把一个bitset左移,右移若干位的结果.(补零);

==,!=:比较两个位数相同的bitset代表的二进制数是否相等;

3.[ ]操作符:
s[k] :表示s的第k位,即可取值也可赋值,编号从0开始;

4.count:
s.count() 返回二进制串中有多少个1;

5.any/none
若s所有位都为0,则s.any()返回false,s.none()返回true;
若s至少有一位为1,则s.any()返回true,s.none()返回false;

6.set/rest/flip
s.set()把s所有位变为1;
s.set(k,v)把s的第k位改为v,即s[k]=v;
s.reset()把s的所有位变为0.
s.reset(k)把s的第k位改为0,即s[k]=0;
s.flip()把s所有位取反.即s=~s;
s.flip(k)把s的第k位取反,即s[k]^=1;

37.斐波那契数列

\begin{bmatrix} F(n) &F(n-1) \end{bmatrix} =   \begin{bmatrix} F(n-1) &F(n-2) \end{bmatrix}  *   \begin{bmatrix} 1 &1 \\ 1& 0 \end{bmatrix} 

\begin{bmatrix} F(n) &F(n-1) \end{bmatrix} =    \begin{bmatrix} 1 &1 \\ 1& 0 \end{bmatrix}^{n-1}

38.倍增思想,类似写法。

ll k;
for(int i=40;~i;i--){
			if(k-(1ll<<i)>=0){
				k-=(1ll<<i);
			}
		}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瘾ิۣۖิۣۖิۣۖิꦿ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值