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.斐波那契数列
= *
=
38.倍增思想,类似写法。
ll k;
for(int i=40;~i;i--){
if(k-(1ll<<i)>=0){
k-=(1ll<<i);
}
}