zoukankan      html  css  js  c++  java
  • NOIP 模拟 921

    DAY 1

    优美的字符串

    题目描述

    给出一个只有'a'和'b'的字符串,希望通过最少的操作后使得所有'b'都在'a'前面。一种操作:每次选取一个'ab'子串变成'bba'。

    答案对1e9+7取模,如果不能得到输出-1.

    n<=1e6

    题解

    可以发现当两个'a'在一起的时候前面的'a'不可能变到后面去(废话),所以可以得出答案一定。

    考虑先移动后面的'a',可以发现一个'a'移动的次数就是后面'b'的个数,并且这次移动后'b'的个数翻倍。

    所以就记录现在'b'的个数和答案就OK了。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=1000005;
    const int mod=1000000007;
    int n;
    char s[maxn];
    ll ans,cnt;//cnt:现在b的个数(包括变换产生的 
    
    int main(){
        freopen("string.in","r",stdin);
        freopen("string.out","w",stdout);
        scanf("%s",s+1);
        n=strlen(s+1);
        for(int i=n;i;i--){
            if(s[i]=='a'){
                ans=(ans+cnt)%mod;//移动cnt就好 
                cnt=2*cnt%mod;//每移动一次都会再带来一个b 
            }
            else cnt=(cnt+1)%mod;
        }
        printf("%lld",ans%mod);
        return 0;
    }
    string

    数字谜题

    诶,我做过。

    为了让这一部分有意义,再介绍一下组合数的方法:

    首先预处理还是一样的,我们需要找出有哪些在[1,1000]内的数可以通过k-1步到达1,然后加入一个vector。

    然后对于加入的每个数进行组合数版的dfs,因为我们知道一共需要多少个1.

    just like number


    城市规划

    题目描述

    给出一个n个点n条边的图,保证联通。问删去一条边之后最长的路径最短是多少。

    对于100% 数据,3<=n<=200000; 1<=w<=10e9

    题解

    CF835F

    考虑暴力就是枚举环上的边,删去之后找直径,求最小的直径。(加上个卡时就看人品了)

    考虑这就是一个基环树,想象将环摆在中间,环上的每个节点有一个树。

    考虑对于删去一条边后什么会成为直径:每颗树的直径或者两个两棵树中到根最长链+两个根的距离。

    第一个是无论我们删哪个边都不变的。

    所以就考虑第二个怎么维护。

    先把环按顺序存下来,假设有m个点,维护每个点到环上第一个点的距离qzh,环上每个点的树中到根最长长度l,如果删去$s_{i},s_{i+1}$的连边:

    设x<y

    在选取1..i的两个点,(lx-qzhx)+(ly+qzhy)

    在i+1...m,和上面一样

    在两部分分别选一个点,tot为环的边权和,tot+(lx+qzhx)+(ly-qzhy)

    为了快速的求解,记录pre为第一种情况的前缀最大值,suf为第二种情况的后缀最大值,对于第三种就记录lx+qzhx,lt-qzhy的前缀最大值即可。

    #include<ctime>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=200005;
    const ll inf=10000000000000000;
    int n,cnt,rt,head[maxn];
    int top,s[maxn],dis[maxn];//存环和点到下个点的距离 
    ll ret,f[maxn];//ret:树中的最大直径 
    bool vis[maxn],circle[maxn];
    struct edge{
        int x,y,next;
        ll val;
    }e[maxn<<1];
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x= f ? -x : x ;
    }
    
    void print(ll x){
        if(x<0) putchar('-'),x=-x;
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    
    ll max(ll x,ll y){return x>y ? x : y ;}
    ll min(ll x,ll y){return x<y ? x : y ;}
    
    inline void add(int x,int y,int val){
        e[++cnt]=(edge){x,y,head[x],val};
        head[x]=cnt;
    }
    
    int dfs(int x,int fa){//找环 
        if(vis[x]) {rt=x;return 1;}
        vis[x]=true;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            if(y==fa) continue;
            int tmp=dfs(y,x);
            if(tmp){
                if(tmp==1){
                 s[++top]=x;
                 dis[top]=e[i].val;
                 circle[x]=true;
                 if(x!=rt) return 1;
              }
              return 2;
            }
        }
        return 0;
    }
    
    ll tot,l[maxn],qzh[maxn];//每颗树中到根最长的链,环上权值和
    ll pre[maxn],suf[maxn];
    ll cpre[maxn],csuf[maxn];
    
    void dfs1(int x,int fa,int root){
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            if(y==fa||circle[y]) continue;
            f[y]=f[x]+e[i].val;
            if(l[root]<f[y]) l[root]=f[y],rt=y;
            dfs1(y,x,root);
        }
    }
    
    void dfs2(int x,int fa,int root){
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            if(y==fa) continue;
            if(circle[y]&&y!=root) continue;
            f[y]=f[x]+e[i].val;
            if(ret<f[y]) ret=f[y];
            dfs2(y,x,root);
        }
    }
    
    void find_d(int x){//树的直径 
        f[x]=0;
        dfs1(x,0,x);
        f[rt]=0;
        dfs2(rt,0,x);
    }
    
    void solve(){
        dfs(1,0);
        for(int i=1;i<=top;i++) find_d(s[i]);
        for(int i=2;i<=top;i++){
            qzh[s[i]]=qzh[s[i-1]]+dis[i];
            tot+=dis[i];
        }
        tot+=dis[1];
        
        ll cur=l[s[1]]-qzh[s[1]];
        for(int i=2;i<=top;i++){
            pre[s[i]]=max(pre[s[i-1]],cur+l[s[i]]+qzh[s[i]]);
            cur=max(cur,l[s[i]]-qzh[s[i]]);
        }
        
        cur=l[s[top]]+qzh[s[top]];
        for(int i=top-1;i;i--){
            suf[s[i]]=max(suf[s[i+1]],cur+l[s[i]]-qzh[s[i]]);
            cur=max(cur,l[s[i]]+qzh[s[i]]);
        }
        
        cpre[s[1]]=l[s[1]]+qzh[s[1]];
        for(int i=2;i<=top;i++)
          cpre[s[i]]=max(cpre[s[i-1]],l[s[i]]+qzh[s[i]]);
        
        csuf[s[top]]=l[s[top]]-qzh[s[top]];
        for(int i=top-1;i;i--)
          csuf[s[i]]=max(csuf[s[i+1]],l[s[i]]-qzh[s[i]]);
        
        ll ans=inf;
        for(int i=1;i<top;i++){
            ll tmp=ret;
            tmp=max(tmp,pre[s[i]]);
            tmp=max(tmp,suf[s[i+1]]);
            tmp=max(tmp,cpre[s[i]]+csuf[s[i+1]]+tot);
            ans=min(ans,tmp);
        }
        printf("%lld",ans);
    }
    
    int main(){
        bool opt=true;
        read(n);
        for(int i=1;i<=n;i++){
            int x,y;
            ll val;
            read(x);read(y);read(val);
            add(x,y,val);
            add(y,x,val);
        }
        solve();
        return 0;
    }
    city

    但是CF没过,T了没看懂。


    DAY 2

    修路

    有一个n个点m条边的无向图,边权为1,现加入一条边,问有多少种加边的方案不能使得s到t的距离变短。

    加边(i,j)和(j,i)算一种,加边原图不能有。

    对于100% 数据,2<=n<=1000; n-1<=m<=2000;1<=S,T<=n; S!=T。保证图联通。

    题解

    一开始想的很简单,因为n很小,求出两两之间的最短距离,用bfs求一次是O(n)的吧。

    然后枚举边(i,j),判断是否有这条边就看dis[i][j]是否为1,为了检验这条路的影响,所以一定到走这条路,所以看s-i-j-y和s-j-i-y是否都不行即可。

    但是发现每次固定的是s,t,而且枚举的边权为1,所以只要知道s到每个点和t到每个点的距离拼接即可,不过在判断是否原图有的时候需要多开一个数组记录。

    #include<ctime>
    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1006;
    const int maxm=2006;
    int n,m,dx,dy;
    int cnt,head[maxn];
    int dis[2][maxn];
    bool mp[maxn][maxn];
    struct edge{
        int x,y,next;
    }e[maxm<<1];
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    void add(int x,int y){
        e[++cnt]=(edge){x,y,head[x]};
        head[x]=cnt;
    }
    
    void bfs(int k,int st){
        queue<int> q;
        dis[k][st]=0;
        q.push(st);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=e[i].next){
                int y=e[i].y;
                if(dis[k][y]==-1){
                    dis[k][y]=dis[k][x]+1;
                    q.push(y);
                }
            }
        }
    }
    
    int main(){
        freopen("road.in","r",stdin);
        freopen("road.out","w",stdout);
        read(n);read(m);read(dx);read(dy);
        for(int i=1;i<=m;i++){
            int x,y;
            read(x);read(y);
            add(x,y);
            add(y,x);
            mp[x][y]=mp[y][x]=true;
        }
        memset(dis,-1,sizeof(dis));
        bfs(0,dx);bfs(1,dy);
        int now=dis[0][dy];
        cnt=0;
        for(int i=1;i<n;i++)
         for(int j=i+1;j<=n;j++)
          if(!mp[i][j])
              if(dis[0][i]+1+dis[1][j]>=now&&dis[0][j]+1+dis[1][i]>=now)
                  cnt++;
        printf("%d",cnt);
    }
    road

    神奇的集合

    题目描述

    有n个神奇的集合,编号为1-n,开始都为空,先进行两种操作:

    将x加入编号[l,r]的集合,如果一个集合本来就有x,那么该集合所有元素个数加倍(并且就不用将这个x加进去

    查询[l,r]的集合的元素和,对998244353取模。

    对于100% 数据,1<=n; q<= 200000,1<=opt<=2,1<=l<=r<=n,1<=x<=n

    题解

    暴力就是各种分治。不过可以发现一点就是如果这个集合本来有一个元素,那么这次就+1,不然就*2.

    所以开n颗线段树维护对于元素i每个位置的情况,再开一个答案线段树。

    考虑区间修改,如果当前区间都没有x,就区间+1,并且记录区间有了x;如果都有x,就区间*2;不然就继续递归。

    一个区间最多被赋值一次,所以时间复杂度O(nlogn)(???)

    #include<map>
    #include<ctime>
    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=200005;
    const int mod=998244353;
    int n,m,cnt;
    int root[maxn],ls[maxn<<5],rs[maxn<<5];
    ll tag[maxn<<5],mul[maxn<<5],sum[maxn<<5];
    int num[maxn<<5];
    bool lazy[maxn<<5];
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    void print(ll x){
        if(x<0) x=-x,putchar('-');
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    
    void update1(int rt){
        sum[rt]=(sum[ls[rt]]+sum[rs[rt]])%mod;
    }
    
    void update2(int rt){
        num[rt]=num[ls[rt]]+num[rs[rt]];
    }
    
    void put_lazy(int &rt,int l,int r){
        if(!rt) rt=++cnt;
        lazy[rt]=true;
        num[rt]=r-l+1;
    }
    
    void push_down2(int rt,int l,int r){
        int mid=(l+r)>>1;
        put_lazy(ls[rt],l,mid);
        put_lazy(rs[rt],mid+1,r);
        lazy[rt]=false;
    }
    
    void put_mul(int &rt,ll val){
        if(!rt) {rt=++cnt;mul[rt]=1;}
        sum[rt]=sum[rt]*val%mod;
        mul[rt]=mul[rt]*val%mod;
        tag[rt]=tag[rt]*val%mod;
    }
    
    void put_tag(int &rt,int l,int r,ll val){
        if(!rt) {rt=++cnt;mul[rt]=1;}
        sum[rt]=(sum[rt]+val*(r-l+1))%mod;
        tag[rt]=(tag[rt]+val)%mod;
    }
    
    void push_down1(int rt,int l,int r){
        int mid=(l+r)>>1;
        if(mul[rt]!=1){
            put_mul(ls[rt],mul[rt]);
            put_mul(rs[rt],mul[rt]);
            mul[rt]=1;
        }
        if(tag[rt]){
            put_tag(ls[rt],l,mid,tag[rt]);
            put_tag(rs[rt],mid+1,r,tag[rt]);
            tag[rt]=0;
        }
    }
    
    void modify(int &rt1,int &rt2,int l,int r,int a_l,int a_r){
        if(!rt1) {rt1=++cnt;mul[rt1]=1;}
        if(!rt2) rt2=++cnt;
        if(a_l<=l&&r<=a_r){
            if(!num[rt2]) {put_lazy(rt2,l,r),put_tag(rt1,l,r,1);return ;}
            else if(num[rt2]==r-l+1) {put_mul(rt1,2);return ;}
        }
        int mid=(l+r)>>1;
        if(lazy[rt2]) push_down2(rt2,l,r);
        push_down1(rt1,l,r);
        if(a_l<=mid) modify(ls[rt1],ls[rt2],l,mid,a_l,a_r);
        if(mid<a_r) modify(rs[rt1],rs[rt2],mid+1,r,a_l,a_r);
        update1(rt1);
        update2(rt2);
    }
    
    ll query(int rt,int l,int r,int a_l,int a_r){
        if(a_l<=l&&r<=a_r) return sum[rt];
        push_down1(rt,l,r);
        ll ret=0;
        int mid=(l+r)>>1;
        if(a_l<=mid) ret+=query(ls[rt],l,mid,a_l,a_r);
        if(mid<a_r) ret+=query(rs[rt],mid+1,r,a_l,a_r);
        return ret%mod;
    }
    
    int main(){
        freopen("multiset.in","r",stdin);
        freopen("multiset.out","w",stdout);
        read(n);read(m);
        for(int i=1;i<=m;i++){
            int opt,l,r;
            read(opt);read(l);read(r);
            if(opt==1){
                int x;read(x);
                modify(root[0],root[x],1,n,l,r);
            }
            else printf("%lld
    ",query(root[0],1,n,l,r));
        }
    }
    multiset

    还有空间开大点。


    缩树游戏

    给出一个n个点的树,每次等概率选一条边(x,y)删去,并且等概率留下其中一个节点,这个节点和所有与x和y连边的点连边。对于每个点求最后剩下的是它的概率。

    2<=n<=50

    题解

    ......

     

  • 相关阅读:
    优美的回文串--全国模拟(二)
    创造新世界--全国模拟(二)
    字符串分类--全国模拟(二)
    平衡数--全国模拟(二)
    最小矩形--全国模拟(二)
    组装三角形--全国模拟(二)
    深入.NET内测题
    面向对象七大设计原则
    创建数据库普通临时表和创建数据库全局变量表和俩者的区别
    使用变量 数据类型转换 逻辑控制语句(begin ...end; case...end; if...else; while)
  • 原文地址:https://www.cnblogs.com/sto324/p/11573625.html
Copyright © 2011-2022 走看看