湖南多销 10

VP

张ls没来,所以就是我和左ls一起打
不得不说,左老师确实牛逼,大B题计算几何他是真会
然后这场我的策略就是跟榜做,然后开到傻比图论题卡死了
然后就是狂爆罚时,所以真的要当后置大脑了吗

题解

E

sb签到题

M

考虑\(1,2,3\)能组成的最大数字
并不显然是\((1+2)\cdot 3=9\)
所以如果\(d\geq 10\)那么这就是显然的
否则随便构造一组像\(97,43,13\)这样子的
三个质数,各自差二三十这样子,然后就可以随便过

G

简单数论题
\(cnt\)\(n\)的十进制位数。
由于\(cnt-1\)位的时候即使每个位都是\(9\)组的数也小于\(n\)
所以每个数至少有\(cnt-1\)
\(cnt\)位每个位都是\(i\)的时候组的数是否大于\(n\)决定是否要多一个。
注意\(0\)至多使用\(cnt-1\)个,所以要特判\(n\geq 10\)的时候答案要减一

L

考虑到多个合法括号序列拼接起来还是合法括号序列
我们只需要找一个断点让它前后两部分都是合法括号序列即可。
这个过程就是括号匹配的过程
然后就是断环成链翻二倍,做个哈希判一下是否相等就行。
题解给的证明是只用看第一个合法断点就行。
如果是哈希的话那就很无脑
注意一定要断环成链不能只判前后

三模哈希
#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int o=2222222,base=233,mod[3]={1000000007,1000000009,998244353};
stack<int>q;
char s[o];
int pos,n,h[3][o],p[3][o];
int P(int x,int y,int m){
    int ans=1;
    while(y){
        if(y&1)ans=ans*x%m;
        x=x*x%m;
        y>>=1;
    }
    return ans;
}
void pre(){
    for(int j=0;j<3;j++){
        p[j][0]=1;
        for(int i=1;i<=n;i++){
            p[j][i]=p[j][i-1]*base%mod[j];
            h[j][i]=(h[j][i-1]+s[i]*p[j][i]%mod[j])%mod[j];
        }
    }
}
bool cmp(int i){
    for(int j=0;j<3;j++){
        if(h[j][n]!=(h[j][i+n]-h[j][i]+mod[j])%mod[j]*P(p[j][i],mod[j]-2,mod[j])%mod[j])return 0;
    }
    return 1;
}
void in(){
    scanf("%s",s+1);
}
void work(){
    pos=0,n=strlen(s+1);
    for(int i=1;i<=n;i++)s[i+n]=s[i];
    n*=2;
    pre();
    n/=2;
    for(int i=1;i<=n;i++){
        if(s[i]=='(')q.push(i);
        else q.pop();
        if(q.empty()){
            if(!cmp(i)){
                pos=i;
                break;
            }
        }
    }
}
void out(){
    if(pos==0)puts("no");
    else{
        for(int i=pos+1;i<=n;i++)printf("%c",s[i]);
        for(int i=1;i<=pos;i++)printf("%c",s[i]);
    }
}
#undef int 
int main(){
    in(),work(),out();
    return 0;
}

I

直接一个set维护所有空莲花,
lower_bound()找一下最小的那个空莲花,直接换掉就行
考虑到事实上空莲花的数量只有\(10^6\),所以相当可行。
时间复杂度\(O(n\log n)\)
然后是一个不会set版的回答。
如果有青蛙为\(1\),没青蛙为\(0\)
那么可以通过前缀和二分答案的方式找到最近的\(pos\)
上一个树状数组维护一下即可
时间复杂度\(O(n\log^2 n)\)
树状数组倍增可以做到\(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int o=2222222;
int n,q,mx,c[o];
struct BIT{
    int t[o];
    int lb(int x){return x&(-x);}
    void add(int x,int val){
        while(x<=mx){
            t[x]+=val;
            x+=lb(x);
        }
    }
    int ques(int x){
        int ans=0;
        while(x){
            ans+=t[x];
            x-=lb(x);
        }
        return ans;
    }
}T;
bool check(int mid,int pos){
    return T.ques(mid)-T.ques(pos-1)==mid-pos+1;
}
void in(){
    cin>>n;mx=2e6;
    for(int i=1;i<=n;i++){
        cin>>c[i];
        T.add(c[i],1);
    }
}
void work(){
    cin>>q;
    for(int i=1,x;i<=q;i++){
        cin>>x;
        int l=c[x],r=mx;
        while(l<r){
            int mid=(l+r)/2;
            if(check(mid,c[x]))l=mid+1;
            else r=mid;
        }
        int ans=l;
        T.add(c[x],-1);
        T.add(ans,1);
        cout<<ans<<"\n";
        c[x]=l;
    }
}
int main(){
    in();work();
    return 0;
}

C

其实只有期望最短的那条才是要找的那条。
所以就是要么走随机传送下期望最短的节点,
要么就是直接不传送走最短路
也没有什么这那的讲究

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int o=2222222;
#define int long long 
struct Graph{
    struct edge{
        int t,n;
    }p[o];
    vector<int>d[o];
    int v[o],h[o],cnt;
    void add(int s,int t){
        cnt++;
        p[cnt].t=t;
        p[cnt].n=h[s];
        h[s]=cnt;
    }
    void bfs(int s,int n){
        memset(v,0,sizeof(v));
        for(int i=0;i<=n;i++)d[s].push_back(0);
        queue<int>q;
        v[s]=1,q.push(s);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=h[x];i;i=p[i].n){
                int y=p[i].t;
                if(v[y])continue;
                d[s][y]=d[s][x]+1;
                v[y]=1;
                q.push(y);
            }
        }
    }
    int ques(int x,int s){
        return d[s][x];
    }
}G;
int n,m,k,fz,fm;
vector<int>l;
bool c[o];
int read(){
    int i=1,j=0;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-')i=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        j=j*10+ch-48;
        ch=getchar();
    }
    return i*j;
}
void in(){
    n=read(),m=read(),k=read();
    for(int i=1,x;i<=k;i++){
        x=read();
        l.push_back(x);
    }
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        G.add(x,y),G.add(y,x);
    }
}
void work(){
    G.bfs(1,n),G.bfs(n,n);
    int val=0;fz=1e18;
    for(auto i:l)val+=G.ques(i,n);
    for(auto i:l)fz=min(fz,val-G.ques(i,n)+(k-1)*G.ques(i,1));
    fz=min(fz,G.ques(1,n)*(k-1));
    fm=(k-1);
    int now=__gcd(fz,fm);
    fz/=now,fm/=now;
}
void out(){
    cout<<fz<<"/"<<fm<<"\n";
}
#undef int 
int main(){
    in(),work(),out();
    return 0;
}

D

这个是一个经典的背包DP
不过要上一个压位高精度
或者考虑一个概率DP的模型也可以。

不过这个投骰子的模型还是太经典了,它非常像二项分布
所以拿二项分布的结论过来写就行了

K

直接考虑合法的部分不能从合法状态推下一个合法状态的转移方程
考虑翻转,设恰好第\(i\)家要造反的方案数\(f_i\)
那么前\(i\)家分配\(\sum\limits_{j=1}^{i}k_j=s_i\)个大数,随便排序
这一部分是\(s_i!\)
然后考虑前\(i-1\)家都不能造反,前面的话就不能全排\(s_{i-1},s_{i-2},s_{i-3},...,s_{1}\)个大数
然后推\(i-1\)的话也是一样,不能排\(s_{i-2},...,s_{1}\)的大数
这样的话递归/记搜的结构就出来了。
如果前\(j\)家要造反的话,那么\(j\)\(i\)的排列是没有限制的
所以,前\(i\)家里前\(j\)家造反的方案数是\(f_j\cdot(s_i-s_j)!\)
两部分结合就可以得到\(f_i=s_i!-\sum\limits^{i}_{j=1}f_j\cdot{(s_i-s_j)!}\)
然后就可以得到最后的答案

\[n!-\sum\limits^{h}_{i=1}f_i\cdot(n-s_i)! \]

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int o=2222222,mod=1e9+7;
int a[o],h,n,s[o],f[o],jc[o],ans;
int dfs(int sum,int pos){
    if(pos==0)return 0;
    if(f[pos]!=-1)return f[pos];
    f[pos]=jc[sum];
    for(int i=1;i<pos;i++)f[pos]=((f[pos]-dfs(s[i],i)*jc[sum-s[i]]%mod)+mod)%mod;
    return f[pos];
}
void in(){
    cin>>n>>h;
    for(int i=1;i<=h;i++){
        cin>>a[i];
        s[i]=a[i]+s[i-1];
    }
}
void work(){
    jc[0]=1;
    for(int i=1;i<=h;i++)f[i]=-1;
    for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
    dfs(s[h],h);
    ans=jc[n];
    for(int i=1;i<=h;i++)ans=((ans-dfs(s[i],i)*jc[n-s[i]]%mod)+mod)%mod;
}
void out(){cout<<ans;}
#undef int 
int main(){
    in(),work(),out();
    return 0;
}

H

首先观察到长度为\(i\)的上下两位都是空的段,
停车方案数\(f_i=f_{i-1}+f_{i-2}\)
就是横着停和竖着停
初值\(f_1=1,f_0=1\)
那这就是长度为\(i\)的方案数是\(fib_i\)
既然知道这些,那么其实剩下的就是找一个集合\(S\),满足

\[\prod\limits_{i\in S}fib_i\equiv n\pmod {10^9+7} \]

接下来就是最牛逼的操作
随机化meet in the middle
首先meet in the middle
\(200\)个数的集合分成前后两半
然后各随机抽\(10^6\)
总共可以抽出\(10^{12}\)种组合
但是值域只有\(10^9+7\)
所以说很难抽不中。
时间复杂度也是稳过的。

posted @ 2025-06-08 15:54  2K22  阅读(1)  评论(0)    收藏  举报