CSP模拟18

CSP模拟18

T1 站队 CF1850H

我们发现如果把怪抽象为节点,如果有两个节点可以用不同长度的路径联通,那么答案不合法,否则答案合法。

我们对每一个没有遍历过的点进行 \(DFS\) ,查找是否有不合法的点对。

注意 \(dis\) 要赋一个极小值/极大值,否则可能使某个点的值恰好为初始值而多次遍历导致超时。

code


#include<iostream>
#include<algorithm>
#include<queue>
#include<cstdio>
#define ll long long
using namespace std;
int n,m,tot,hea[400010],nex[400010],to[400010];
ll wa[400010],dis[400010];
bool f;
void add(int x,int y,ll z){
    to[++tot]=y;
    wa[tot]=z;
    nex[tot]=hea[x];
    hea[x]=tot;
}
void dfs(int x){
    if(f==1) return;
    for(int i=hea[x];i;i=nex[i]){
        int t=to[i];
        if(f==1) return;
        if(dis[t]==-1e17){
            dis[t]=dis[x]+wa[i];
            dfs(t);
        }
        else{
            if(dis[t]!=dis[x]+wa[i]){
                f=1;
                return ;
            }
        }
    }
}
void work(){
    scanf("%d%d",&n,&m);
    f=0;tot=0;
    for(int i=1;i<=n;i++) hea[i]=0;
    for(int i=1;i<=m;i++){
        int a,b;
        ll d;
        scanf("%d%d%lld",&a,&b,&d);
        add(a,b,d);
        add(b,a,-d);
    }
    for(int i=1;i<=n;i++) dis[i]=-1e17;
    for(int i=1;i<=n;i++){
        if(dis[i]==-1e17){
            dis[i]=0;
            dfs(i);
            if(f==1) break;
        } 
    }
    if(f==1) printf("NO\n");
    else printf("YES\n");
    return;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--) work();
    return 0;
}

T2 打怪兽 CF1852C

我们把怪的生命值转化为矩形高度,红色部分为怪的生命值,黑色直线为选择的区间。我们发现只有后面怪的生命值相对于前一个怪增加时才会增加代价。

对于怪的生命值有两种情况,设怪的生命值分别为 \(a_1\)\(a_2\)

  • 前一个怪生命值小于后面一个怪。\(a_1 < a_2\)

  • 前一个怪的生命值大于后面一个怪。\(a_1 > a_2\)

对于第一种情况,我们可以让这个怪前方一部分加 \(k\) ,转换为第二种情况。或者答案加上后一个怪和前一个怪的生命值的差值,代价是答案加上 \(a_2-a_1\)

对于第二种情况,我们可以在此时把后面的怪的生命值增加 \(k\) ,代价是答案增加 \(a_2 + k-a_1\) ,或者不进行任何操作。

我们可以把每一种方案的代价放入优先队列中,每次遇到第一种情况时选出代价最小的方案统计到答案中。

code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
int n,k,a[200010];
ll ans;
int work(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]%=k;
    }
    priority_queue<int,vector<int>,greater<int> >q;
    for(int i=1;i<=n;i++){
        if(a[i]>a[i-1]){
            q.push(a[i]-a[i-1]);
            ans+=q.top();
            q.pop();
        }
        if(a[i]<a[i-1]){
            q.push(a[i]+k-a[i-1]);
        }
    }
    printf("%lld\n",ans);
    ans=0;
    return 0;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        work();
    }
}

T3 不用斯特林数

code


#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
int n,a[5000010],ma,ans;
int mu[10000010],tot,pri[5000010],mi[5000010];
int f[10000010],g[10000010];
bool v[10000010];
void getmu(){
    mu[1]=1;
    for(int i=2;i<=ma;i++){
        if(v[i]==0){
            pri[++tot]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=tot&&i*pri[j]<=ma;j++){
            v[i*pri[j]]=1;
            if(i%pri[j]==0){
                break;
            }
            mu[i*pri[j]]=-mu[i];
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        ma=max(ma,a[i]);
        f[a[i]]++;
    }
    getmu();
    mi[0]=1;
    for(int i=1;i<=n;i++){
        mi[i]=(2ll*mi[i-1])%mod;
    }
    for(int i=1;i<=tot;i++){
        for(int j=ma/pri[i];j>=1;j--){
            f[j]+=f[pri[i]*j];//狄利克雷后缀和
        }
    }
    for(int i=1;i<=ma;i++){
        g[i]=mu[i]*f[i];
        f[i]=mi[f[i]]-1;
    }
    for(int i=1;i<=tot;i++){
        for(int j=1;j*pri[i]<=ma;j++){
            g[pri[i]*j]+=g[j];//狄利克雷前缀和
        }
    }
    for(int i=1;i<=tot;i++){
        for(int j=1;j*pri[i]<=ma;j++){
            f[j]-=f[pri[i]*j];//倒狄利克雷后缀和
            f[j]=(f[j]+mod)%mod;
        }
    }
    for(int i=2;i<=ma;i++){
        ans+=1ll*f[i]*g[i]%mod;
        ans%=mod;
    }
        printf("%d\n",ans);
    return 0;
}

posted @ 2023-08-11 16:54  muzqingt  阅读(25)  评论(1编辑  收藏  举报