一拓扑排序:

#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]);
    }
posted on 2023-03-15 20:04  IR101  阅读(3)  评论(0编辑  收藏  举报  来源