一拓扑排序:
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int idx,ne[2*N],e[2*N],h[N];
int d[N];
int ans[N],cnt;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int main(){
int n,m;
cin>>n>>m;
memset(h,-1,sizeof h);
while (m -- ){//依次读入边
int a, b;
cin >> a >> b;
d[b]++;//顶点b的入度+1
add(a, b);//添加到邻接矩阵
}
queue<int>q;
for(int i=1;i<=n;i++){
if(!d[i])
q.push(i);
}
while(!q.empty()){
int t=q.front();
q.pop();
ans[cnt++]=t;
for(int i=h[t];i!=-1;i=ne[i]){
int b=e[i];
d[b]--;
if(!d[b])
q.push(b);
}
}
if(cnt==n){
for(int i=0;i<n;i++){
cout<<ans[i]<<' ';
}
}
else
cout<<"-1";
}
22
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int dp[N];
int d[N];
vector<int>g[N];
vector<int>ans;
void topo() // 拓扑排序
{
priority_queue<int,vector<int>,greater<int> >q; // 编号小的在前
while(!q.empty())
q.pop();
ans.clear();
for(int i=1;i<=n;i++)
if(!d[i])q.push(i);
while(!q.empty())
{
int u=q.top();q.pop();
ans.push_back(u);
int sz=g[u].size();
for(int i=0;i<sz;i++)
{
int v=g[u][i];
d[v]--;
if(d[v]==0)q.push(v);
}
}
}
int main()
{
int t;
ios::sync_with_stdio(false);
cin>>t;
while (t--) {
while (cin >> n >> m) {
for (int i = 1; i <= n; i++)
g[i].clear();
memset(d, 0, sizeof(d));
int x, y;
for (int i = 1; i <= m; i++) {
cin >> x >> y; // 不去重边也没事,可以自己模拟一下
g[x].push_back(y);
d[y]++;
}
topo();
for (int i = 0; i < n; i++)
i == n - 1 ? printf("%d\n", ans[i]) : printf("%d ", ans[i]);
}
}
return 0;
}
二:把一个数分成质数相乘的形式
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,x;
const int mod=1e9+7;
unordered_map<ll,ll>has;
int main(){
cin>>n;
while (n--){
cin>>x;
for (int i = 2; i <=x/i ; ++i) {
while (x%i==0){
has[i]++;
x/=i;
}
}
if(x>1)
has[x]++;
}
ll cnt=1;
for( auto p : has ){
cnt =cnt * (p.second+1)%mod;
}
cout<<cnt;
}
三:线性筛法求质数
#include<bits/stdc++.h>
using namespace std;
int prime[1000006],cnt;
bool st[1000006];
void get_prime(int n){
for(int i=2;i<=n;i++){
if(!st[i]){
prime[cnt++]=i;
}
for(int j=0;i*prime[j]<=n;j++){
st[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
int main(){
int n;
cin>>n;
get_prime(n);
cout<<cnt;
}
四:优先队列:
priority_queue<int,vector<int>,greater<int> >q[100005];
map<int ,int >a;
五:P8742 [蓝桥杯 2021 省 AB] 砝码称重
#include<bits/stdc++.h>
using namespace std;
int dp[101][100001],n,s,a[105];
int main(){
cin>>n;
for (int i = 1; i <=n ; ++i) {
cin>>a[i];
dp[i][a[i]]=1;
s+=a[i];
}
for (int i = 1; i <=n ; ++i) {
for (int j = s; j >=0 ; --j) {
if(dp[i-1][j]){
dp[i][j]=dp[i][j+a[i]]=dp[i][abs(j-a[i])]=1;
}
}
}
int cnt=0;
for (int i = 1; i <=s ; ++i) {
if(dp[n][i])
cnt++;
}
cout<<cnt;
}
六:djstl
#include<bits/stdc++.h>
using namespace std;
const int N = 510;
int n;
int vis[N],d[N],g[N][N];
void dijkstra(){
memset(vis,0,sizeof(vis));
memset(d,0x3f,sizeof(d));
d[1]=0;
for(int i=1;i<=n;i++){
int t=-1;
for(int j=1;j<=n;j++)
if(!vis[j]&&(t==-1||d[j]<d[t]))
t=j;
vis[t]=1;
for(int j=1;j<=n;j++)
d[j]=min(d[j],d[t]+g[t][j]);
}
if(d[n]==0x3f3f3f3f) d[n]=-1;
}
signed main(){
int u,v,w;
memset(g,0x3f,sizeof(g));
int m;
cin>>n>>m;
while(m--){
cin>>u>>v>>w;
g[u][v]=min(w,g[u][v]);
}
dijkstra();
cout<<d[n];
}
堆优化版本的djstl:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int,int>
vector<PII>mp[150005];
int n,m;
int dist[150001];
bool st[155001];
void djstl(){
for (int i = 1; i <=n ; ++i) {
dist[i]=(int)1e15;
}
priority_queue<PII,vector<PII>,greater<PII>>q;
dist[1]=0;
q.push({0,1});
while(!q.empty()){
auto t=q.top();
q.pop();
int ver=t.second,ds=t.first;
if(st[ver])continue;
st[ver]= true;
for (int i = 0; i <mp[ver].size() ; ++i) {
auto p=mp[ver][i];
if(dist[p.first]>ds+p.second){
dist[p.first]=ds+p.second;
q.push({dist[p.first],p.first});
}
}
}
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for (int i = 0; i <m ; ++i) {
int x,y,z;
cin>>x>>y>>z;
mp[x].push_back({y,z});
}
djstl();
if(dist[n]==(int)1e15)cout<<"-1";
else
cout<<dist[n];
}
(PTA自主训练3)7-14 直捣黄龙
思路:利用map映射字符串,建图,跑一遍堆优化的djstl,求最短路,然后用dfs从起点跑向终点,速度相同取ans.size()最大,其次再取cnt最大
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,s,e;
string sta,ed;
int val[205],dis[205],vis[205];
vector<pair<int,int>>g[205];
#define PII pair<int,int>
unordered_map<string ,int >sti;
unordered_map<int,string >its;
vector<int >ans,now;
int ansdis=-1,anscnt=-1,anstimes;
void djstl(){
fill(dis+1,dis+1+n,INT_MAX);
dis[e]=0;
priority_queue<PII,vector<PII>,greater<PII>>q;
q.emplace(0,e);
while (!q.empty()){
auto [d,u]=q.top();
q.pop();
if(vis[u])continue;
vis[u]=1;
for(auto [v,w]:g[u]){
if(vis[v])continue;
if(dis[v]<=d+w)continue;
dis[v]=d+w;
q.emplace(dis[v],v);
}
}
}
void dfs(int x,int d,int cnt){
if(d+dis[x]>ansdis)return;
if(x==e&&d==ansdis){
anstimes++;
if(now.size()>ans.size()){
anscnt=cnt,ans=now;
}
else if(now.size()==ans.size()&&cnt>anscnt){
anscnt=cnt,ans=now;
}
return;
}
for(auto [v,w]:g[x]){
if(vis[v])continue;
now.push_back(v),vis[v]=1;
dfs(v,d+w,cnt+val[v]);
now.pop_back();vis[v]=0;
}
return;
}
int main(){
cin>>n>>k>>sta>>ed;
sti[sta]=1,its[1]=sta;
for (int i = 2; i <=n ; ++i) {
cin>>its[i]>>val[i],sti[its[i]]=i;
}
s=sti[sta],e=sti[ed];
while(k--){
int u,v,w;
string us,vs;
cin>>us>>vs>>w;
u=sti[us],v=sti[vs];
g[u].push_back({v,w});
g[v].push_back({u,w});
}
djstl();
fill(vis, vis + 1 + n, 0);
ansdis=dis[s],vis[s]=1;
dfs(s,0,0);
cout<<sta;
for (auto i:ans) {
cout<<"->"<<its[i];
}
cout<<"\n"<<anstimes<<' '<<ansdis<<' '<<anscnt<<endl;
return 0;
}
七.二分查找算法模板:
二分模板一共有两个,分别适用于不同情况。 算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。
版本1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。
代码:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
版本2
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。
代码:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
int a[5]={1,2,5,8,9};
cout<<upper_bound(a,a+5,8)-a-1;//ans==3,返回的大于8的位置
八:快速幂
ll ksm(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b=b>>1%mod;
}
return res;
}
九:spaf算法(存在负权边)单源最短路
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[N],ne[N],h[N],w[N],d[N],idx,n,m;
bool st[N];
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void spfa(){
memset(d,0x3f,sizeof d);
d[0]=0;
queue <int> q;
q.push(0);
st[0]=true;
while(q.size()){
auto t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]>d[t]+w[i]){
d[j]=d[t]+w[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
int x,y,z;
while(m--){
cin>>x>>y>>z;
add(x,y,z);
}
spfa();
int k=0,s=0;
for(int i=1;i<=n;i++){
if(d[i]>s){
s=d[i];
k=i;
}
}
cout<<s<<' '<<k;
return 0;
}
十:并查集
int find(int x){
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
void unity(int x, int y)
{
int r1 = find(x);//找到x的祖先
int r2 = find(y);//找到y的祖先
fa[r1] = r2;//祖先和祖先结为父子(谁是父亲谁是儿子都可以)
}//合并
十一:权值线段树
#include <bits/stdc++.h>
using namespace std;
#define N 200010
int sum[N*4];
void pushup(int rt) {
sum[rt] = sum[rt*2] + sum[rt*2 + 1];
}
//父亲等于俩儿子的和。
void build(int l,int r,int rt) {
if (l==r) {
sum[rt] = 0;
return;
}
int m = (l + r) >> 1;
build(l,m,rt*2);
build(m + 1,r,rt * 2 + 1);
pushup(rt);
}
///建线段树
void update(int p,int add,int l,int r,int rt) {
if(l==r) {
sum[rt] += add;
return;
}
///回溯更新sum
int m = (l + r) >> 1;
if(p <= m) update(p,add,l,m,rt*2);
else update(p,add,m + 1,r,rt * 2 + 1);
pushup(rt);
}
////
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int m = (l + r) >> 1;
int ans1 = 0, ans2 = 0;
if (L <= m) ans1 += query(L,R,l,m,rt * 2);
if (R > m) ans2 += query(L,R,m + 1,r,rt * 2 + 1);
return ans1 + ans2;
}
/////计算l到r出现过几次
int main() {
int n;
scanf("%d",&n);
build(1,100000,1);
int ss=0;
int s=0;
for(int i=1; i<=n; ++i) {
int a;
scanf("%d",&a);
update(a,1,1,100000,1);
//printf("rt = %d\n", sum[1]);
int da=query(a + 1,100000,1,100000,1);
int g= query(1,a,1,100000,1);
int k= query(a,a,1,100000,1);
ss+=g-k-da;
s=max(s,ss);
}
cout<<s<<' '<<ss<<endl;
return 0;
}
十二:线段树::
1 将某区间每一个数加上 k。
2 求出某区间每一个数的和。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int a[maxn+2];
struct tree{
int l,r;
long long pre,add;
}t[4*maxn+2];
void bulid(int p,int l,int r){
t[p].l=l;t[p].r=r;
if(l==r){
t[p].pre=a[l];
return;
}
int mid=l+r>>1;
bulid(p*2,l,mid);
bulid(p*2+1,mid+1,r);
t[p].pre=t[p*2].pre+t[p*2+1].pre;
}
void spread(int p){
if(t[p].add){
t[p*2].pre+=t[p].add*(t[p*2].r-t[p*2].l+1);
t[p*2+1].pre+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
t[p*2].add+=t[p].add;
t[p*2+1].add+=t[p].add;
t[p].add=0;
}
}
void change(int p,int x,int y,int z){
if(x<=t[p].l && y>=t[p].r){
t[p].pre+=(long long)z*(t[p].r-t[p].l+1);
t[p].add+=z;
return;
}
spread(p);
int mid=t[p].l+t[p].r>>1;
if(x<=mid) change(p*2,x,y,z);
if(y>mid) change(p*2+1,x,y,z);
t[p].pre=t[p*2].pre+t[p*2+1].pre;
}
long long ask(int p,int x,int y){
if(x<=t[p].l && y>=t[p].r) return t[p].pre;
spread(p);
int mid=t[p].l+t[p].r>>1;
long long ans=0;
if(x<=mid) ans+=ask(p*2,x,y);
if(y>mid) ans+=ask(p*2+1,x,y);
return ans;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
bulid(1,1,n);
for(int i=1;i<=m;i++)
{
int q,x,y,z;
scanf("%d",&q);
if(q==1){
scanf("%d%d%d",&x,&y,&z);
change(1,x,y,z);
}
else {
scanf("%d%d",&x,&y);
cout<<ask(1,x,y)<<endl;
}
}
return 0;
}
区间合并求并集
void solve(){
scanf("%d%d",&l,&m);
for(int i=0;i<m;i++){
scanf("%d%d",&q[i].first,&q[i].second);
}
sort(q,q+m);
int ls=0,now=0;
int ans=0;
for(int i=0;i<m;i++){
now=q[i].first;
ans+=max(0,now-ls);
ls=max(ls,q[i].second+1);
}
ans+=max(0,l-ls+1);
cout<<ans;
}
ST表:
- 空间复杂度:O(nlogn),单词询问O(1),预处理时间复杂度O(nlogn);
- d【i】【j】表示左端点i,长度为2^j的区间的最值问题 他管辖了【i】【i+2^j-1】这个区间
- d[5][3]=MIN(d[5][2],d[9][2])
- min(5,12)=min(5, 8)和min(9,12)取min
- 询问[5,10] 区间长度为6,log6为log[6>>1]+1=2;
- 则[5,10]=min(5到8)min(7到10);
- [5,10]=min(d[5][2],d[7][2]);
则 k=lg[r-l+1],ans=min(d[l][k],d[r-(1<<k)+1][k])
求最大值,同理
代码:
#include<bits/stdc++.h> using namespace std; #define int long long int d1[500006][20]; int d2[500006][20]; int q[500006]; int lg[500006]; int n,m; void stb(){ lg[0]=-1 , lg[1]=0; for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1; for(int j=1;j<=20;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { cout<<i<<' '<<i+((1<<j))-1<<endl; cout<<i<<' '<<(i+(1<<(j-1)))-1<<" ** "<<i+(1<<(j-1))<<' '<<i+(1<<(j-1))+(1<<(j-1))-1<<endl<<endl; d1[i][j]=min(d1[i][j-1],d1[i+(1<<(j-1))][j-1]); d2[i][j]=max(d2[i][j-1],d2[i+(1<<(j-1))][j-1]); } } } int mi(int l,int r){ int k=lg[r-l+1]; return min(d1[l][k],d1[r-(1<<k)+1][k]); } int ma(int l,int r){ int k=lg[r-l+1]; return max(d2[l][k],d2[r-(1<<k)+1][k]); }