VP abc137

A. +-x

  • 模拟题
void solve(){
    cin>>a>>b;
    cout<<max({a+b,a*b,a-b})<<endl;
}

B. One Clue

  • 模拟题
void solve(){
    cin>>k>>x;

    if(k==1){
        cout<<x<<endl;
        return;
    }

    for(int i=x-k+1;i<=x+k-1;i++){
        cout<<i<<' ';
    }

}

C. Green Bin

  • 题意:给出变位词概念,叫我们求出变位词对的个数。

  • 解题:变位词本质上就是排完序是同一个单词,然后通过交换其中几个词。题目要求变位词对的个数,我们可以先统计排完序单词的个数,然后按照组合数 $C_{cnt}^{2}$ 来计算最后的答案即可。

void solve(){
    cin>>n;

    map<string,int>S;

    for(int i=1;i<=n;i++){
        cin>>s;
        sort(s.begin(),s.end());
        S[s]++;     
    }

    int ans=0;

    for(auto& [x,y]:S)ans+=(y-1)*y/2;

    cout<<ans;

}

D. Summer Vacation

  • 题意:有 $n$ 个工会,如果你接受第 $i$ 个工作并完成它,你将在完成当天之后的 $A_i$ 天内获得奖励 $B_i$ 并且每天最多只能接受并完成一个工作,同时不能重新接受已经完成过的工作,求不迟于 $M$ 天内能获得的最大奖励数。

  • 解题:要求最大奖励数,其实可以想到:尽可能选时间短的奖励多的,因此可以先对 $A_i$ 从小到大排序。那么之后我们肯定是尽可能选奖励多的,因此我们可以开一个大顶堆,把所有截至到第 $i$ 天之前的奖励数全部放入堆中,然后取出最大的即可。这样就能保证我们的贪心是正确的。

void solve(){
    cin>>n>>m;

    for(int i=1;i<=n;i++){
        cin>>w[i].x>>w[i].y;
    }
    
    sort(w+1,w+1+n);

    priority_queue<int>q;

    int t=1;
    int res=0;
    for(int i=1;i<=m;i++){
        while(t<=n&&w[t].x<=i){
            q.push(w[t++].y);
        }
        if(q.size()){
            res+=q.top();
            q.pop();
        }
    }

    cout<<res<<endl;
}

这就是具有二维变量的贪心,有的时候可能是用 dp 去做,但本题数据规模较大,因此得想到贪心,这种常见的思路一般是先定一个顺序然后另一个顺序可以用类似 双指针单调队列 等优化和计算。

E. Coins Respawn

  • 题意:给你一张图,你从 $1$ 号点出发,要到 $n$ 号点,图上的每一条边都有硬币,你要收集它们,当然当你收集了一次之后在这条边又会出现这个硬币。最后到 $n$ 号节点时要上交 $T\times P$ 个硬币,其中 $T$ 是走的时间,$P$ 是给定值。求最后获得到的硬币数最多是多少。

  • 思路:因为最后都要上交 $T\times P$ 个硬币,那不如简化运算,然所有权值都减去 $P$,这样最后就可以不用上交了。然后我们再考虑什么情况下会无解,也就是答案无限大的情况,那就是有一个边权和为正数的环与点 $n$ 联通,这里就先建立个反图,从 $n$ 出发,标记能走到的点,然后 spfa 的时候不去松弛它们,这样就能保证用spfa判断正环的时候包括 $n$ 这个点了(很重要,要不然过不了第三个测试点)。然后要求最大的答案,此时就是求从 $1\rightarrow n$ 的最长路,注意这里有可能有负权边,因此必须用 spfa

int n,m,p;
vector<PII>G[N];
vector<int>g[N];
bool vis[N],st[N];
int cnt[N],dist[N];

void dfs(int u){
    if(vis[u])return;
    vis[u]=1;
    for(auto j:g[u])dfs(j);
}

void spfa(){
    memset(dist,-0x3f,sizeof dist);
    dist[1]=0,st[1]=1;
    queue<int>q;
    q.push(1);

    while(q.size()){
        int t=q.front();
        q.pop();
        st[t]=0;
        for(auto [j,w]:G[t]){
            if(!vis[j])continue;
            if(dist[j]<dist[t]+w){
                dist[j]=dist[t]+w;
                if(!st[j]){
                    cnt[j]++;
                    if(cnt[j]>n){
                        cout<<"-1"<<endl;
                        exit(0);
                    }
                    q.push(j);
                    st[j]=1;
                }
            }
        }
    }
}

void solve(){
    cin>>n>>m>>p;

    for(int i=1;i<=m;i++){
        int a,b,c;
        cin>>a>>b>>c;
        G[a].emplace_back(b,c-p);
        g[b].emplace_back(a);
    }

    dfs(n);
    spfa();

    cout<<max(dist[n],0ll)<<endl;

}

看似简单,实则细节满满。

F. Polynomial Construction 构造+多项式

  • 题意:给出长度为 $p$ 的 $01$ 序列 $a$, 构造 $f(x)=\sum\limits_{i=0}^{p-1} b_i x^i$,满足 $f(i) \equiv a_i \pmod{p}$。

  • 思路:可以看出 $f(x)$ 是一个多项式,此时可以考虑二项展开式。又因为此时的 $a$ 只有 $01$ 两种取值,因此可以:在 $a_x = 1$ 时构造一个函数 $g(i)$ 使 $i = x$ 时 $g(i) = 1$,$i \not = x$ 时 $g(i)=0$。这样使求和后只对 $f(x)$ 产生影响。由于题目要求是在模意义下(又因为 $p$ 为质数),且值为 $1$,可以考虑用费马小定理构造出 $g(i) = 1 - (i-x)^{p-1}$,后面的 $(i-x)^{p-1}$ 用二项式定理展开得 $\displaystyle\sum_{j=0}^{p-1} \binom{p-1}{j}i{p-j-1}(-x)i$。

思路参考

void solve(){
    cin>>p;

    for(int i=0;i<=p;i++){
        for(int j=0;j<=i;j++){
            if(!j)C[i][j]=1;
            else C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
        }
    }

    for(int i=0;i<p;i++){
        cin>>a[i];
        if(!a[i])continue;
        int f[N]={1},now[N]={};
        for(int j=1;j<p;j++)f[j]=(f[j-1]*i)%p;
        for(int j=0;j<p;j++){
            if((p-j)&1)now[j]=(C[p-1][j]*f[p-1-j])%p;
            else now[j]=-(C[p-1][j]*f[p-1-j])%p;
        }
        for(int j=0;j<p;j++)b[j]=(b[j]-now[j]+p)%p;
        b[0]=(b[0]+1)%p;
    }
    for(int i=0;i<p;i++)cout<<b[i]<<' ';

}

多项式练少了,想不到这方面。

posted @ 2025-03-13 00:11  shuying6  阅读(60)  评论(0)    收藏  举报