CSP模拟赛20190922

目录

  • DAY1
    • T1
    • T2
    • T3
  • DAY2
    • T1
    • T2
    • T3

@(CSP模拟赛20190922)
两天的一二题都挺水的,期望本来可以得400,但是没有一天是得完了能得的分的。

DAY1

T1

优美的字符串(string)
【题目描述】
小 Y 送给小 F 一个字符串作为礼物,这个字符串只由’a’ 和’b’ 组成。
由于小 F 患有严重的强迫症,他觉得这个字符串并不优美,他决定对它做一些
操作:
每次操作从字符串中选择一个’ab’ 子串,并将其替换为’bba’。
如果一个字符串的所有’b’ 都在所有’a’ 前面,他认为这个字符串是优美的。
现在小 F 想知道,最少需要多少次操作,能使这个字符串是优美的,或者这个字
符串不可能变成优美的。
【输入格式】
从文件 string.in 中读入数据。
一行一个只由’a’ 和’b’ 组成的字符串。
【输出格式】
输出到文件 string.out 中。
输出一行一个整数,如果无解,输出“-1”。否则输出最少操作次数对.
1000000007
取. 模. 。

大水题,手推五分钟就切了。

#include
using namespace std;
#define ll long long
const int N=1000007;
const ll mod=1000000007;
ll dis[N];
char c[N];
templateinline void read(T &res){
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
    while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
}
int main()
{
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%s",c);
    int n=strlen(c),tot=0;
    ll ans=0;
    c[n]='a';
    for(int i=n-1,last=n;i>=0;--i)
        if(c[i]=='a'){
            dis[++tot]=last-i-1;
            last=i;
        }
    for(int i=1;i<=tot;++i){
        ans=(ans+dis[i])%mod;
        dis[i+1]=(dis[i]*2+dis[i+1])%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

T2

【题目描述】
小 X 同学有很强的计算能力,现在他正在玩一个游戏。
现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的
个数。
小 X 不断的进行操作,直到这个数变成 1 为止。
由于小 X 的计算能力很强,他现在给出一个 n,他想知道有多少不超过 n 的正整
数会在 k 次操作后变成 1。由于答案可能很大,请对 1000000007 取模。
【输入格式】
从文件 number.in 中读入数据。
第一行一个用二进制表示的正整数 n,含义如题目描述。
第二行一个整数 k, 含义如题目描述。
【输出格式】
输出到文件 number.out 中。
输出一个整数,表示答案对 1000000007 取模的值。

发现2^1000经过一次计算就降到了1000一下,所以可以1000一下暴力搜索,然后再用组合数求值。
记得特判下k=0和1的情况

#include
using namespace std;
#define ll long long
const int N=10007;
const ll mod=1000000007;
int n,k,lim,num[N];
ll ans,fac[N]={1};
char ch[N];
ll fp(ll x,ll k){
    ll ans=1,s=x;
    while(k){
        if(k&1) ans=ans*s%mod;
        k>>=1;
        s=s*s%mod;
    }
    return ans;
}
inline ll inv(ll x){return fp(x,mod-2);}
inline ll C(ll n,ll m){
    if(n>m||n<0||m<0) return 0;
    return fac[m]*inv(fac[n])%mod*inv(fac[m-n])%mod;
}
ll cal(int x){
    ll res=0,cnt=0;
    for(int i=0;ilim) return;
    for(int i=max(fp(2,onenum)-1,2ll);i<=n;++i){
        int cnt=0,temp=i;
        while(temp){
            if(temp&1) ++cnt;
            temp>>=1;
        }
        if(cnt==onenum) dfs(i,stp+1);
    }
}
int main()
{
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    scanf("%s",ch);
    n=strlen(ch);
    scanf("%d",&k);
    if(k==0){
        printf("1\n");
        return 0;
    }
    for(int i=0;i

T3

【题目描述】
S 市的有着非常特殊的城市规划,可以把它抽象成有 n 个点 n 条边的连通图。
S 市的市长对这样的城市规划十分不满意,他想删去其中的一条路使得任意两点间
有且仅有一条简单路径。
S 市的市民对这样的城市规划也十分不满意,具体地,市民的不满意度为任意两点
间最短距离的最大值。
现在小 X 想知道,删去满足条件的一条道路后,市民不满意度的最小值。
【输入格式】
从文件 city.in 中读入数据。
第一行一个正整数 n,表示城市的点数和边数。
接下来 n 行,每行三个正整数 ui
, vi
,wi,表示每条边的两个端点和长度。
【输出格式】
输出到文件 city.out 中。
输出一个整数,表示答案

考虑删掉一条边后,什么样的一条链会成为直径:
把这个图看成是很多棵树由一个环相连:
1.一棵树的直径就是这个图的直径。
2.两个树最深的两个点之间的路径。
删掉环上一条边显然不会影响第一种情况,考虑最小化第二种情况:
假设环的长度为k,环上的点为a1,a2,...,ak,令a0=ak。
令si表示ai到a1的距离。显然这是一个前缀和。
以这个点为根的树上最深的点深度为dep1,dep2,…,depk。
如果断开的路径为e(a{i-1},ai),分为三种情况:
1.从1-i-1选两个点能取到最大值。
2.从i-k选两个点取到最大值。
3.前后各选一个点取到最大值。
对于每条边,我们需要快速的求出三种情况的max。
对于第一种情况,两点x,y间的距离为depx+depy+(sy-sx)=(depx-sx)+(depy+sy)
对于第二种情况结果同第一种情况。
对于第三种情况:设x 我们需要让三种情况的最大值最小。考虑求每种情况的最大值:
预处理depi-si,depi+si,维护前缀和后缀最大值,就能得到每种情况的最大值,记录所有最大值的最小值,就是答案。

反正我不会

DAY2

T1

【题目描述】
为缓解 S 城与日俱增的交通压力,S 城的市长准备修一条路。
S 城共有 n 个街区,它们由 m 条双向道路相连,每条道路的长度相等。
作为 S 城的天才,小 X 了解到 S 城的交通压力主要来自于最繁华的 S 街区和 T
街区。如果新修的路不能使 S 街区到 T 街区的距离缩短,就不能缓解 S 城的交通压力。
S 城的市长自然不了解这一点。现在小 X 想知道,有多少种修路方案不能缓解 S
城的交通压力。
注意,对于修路方案 (ui
, ti) 和 (ti
, ui) 视为同一种方案,新修的路不能在原图中存在。
【输入格式】
从文件 road.in 中读入数据。
第一行四个整数 n, m, S, T。表示 S 城的街区数,道路数,繁华的两个街区的编号。
接下来 m 行,每行两个数 ui
, vi 表示一条道路上的两个街区。
图中无重边和自环。
【输出格式】
输出到文件 road.out 中。
输出一行一个整数。表示不能缓解交通压力的方案数。

bfs求任意两点间的最短路,然后暴力枚举任意两点连边是否可以使最短路变短(类似dijsktra和floyed的松弛操作)

#include
using namespace std;
#define ll long long
const int N=2007;
int n,m,S,T,tot,ans,head[N],dis[N][N];
bool vis[N],edge[N][N];
struct Edge{
    int to,next;
}e[N<<1];
void add(int from,int to){
    e[++tot].to=to;
    e[tot].next=head[from];
    head[from]=tot;
}
templateinline void read(T &res){
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
    while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
}
queue q;
void bfs(int sta){
    memset(vis,false,sizeof(vis));
    q.push(sta);
    vis[sta]=true;
    while(q.size()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(vis[v]) continue;
            vis[v]=true;
            dis[sta][v]=dis[sta][u]+1;
            q.push(v);
        }
    }
}
int main()
{
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    read(n);read(m);read(S);read(T);
    for(int i=1;i<=m;++i){
        int u,v;
        read(u);read(v);
        add(u,v);add(v,u);
        edge[u][v]=edge[v][u]=true;
    }
    for(int i=1;i<=n;++i) bfs(i);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j){
            if(edge[i][j]) continue;
            if(dis[S][T]>dis[S][i]+dis[j][T]+1||dis[S][T]>dis[S][j]+dis[i][T]+1){
                ++ans;
//              printf("%d %d\n",i,j);
            }
        }
    printf("%d\n",n*(n-1)/2-m-ans);
    return 0;
}

T2

【题目描述】
everlasting 有 n 个神奇的集合,编号为 1n。开始时它们都是空的,现在 everlasting
要对它们进行两种操作:

  1. 将元素 x 加入编号 [l,r] 的集合中。神奇的是,如果一个集合原本就有 x,那么
    该集合中所有元素的个数都会翻倍
  2. 询问编号 [l,r] 的集合元素个数的和,对 998244353 取模。
    everlasting 当然不会做啦,但是他想考考你...
    【输入格式】
    从文件 multiset.in 中读入数据。
    第一行两个正整数 n, q, 表示集合个数和询问数量。
    接下来 q 行,首先是一个整数 opt:
    若 opt = 1,接下来三个整数 l,r, x,表示向编号 [l,r] 的集合中加入 x。
    若 opt = 2,接下来两个整数 l,r,表示询问编号 [l,r] 的集合的元素个数和。
    【输出格式】
    输出到文件 multiset.out 中。
    对于每个询问,输出一行一个整数,表示答案。

显然可以开n个线段树来维护n个元素的覆盖情况,动态开点防止空间爆炸。剩下就很简单了。

考场调了2.5h也没打出来,旁边的myg大佬一下就打完了,再此膜拜。

#include
using namespace std;
#define ll long long
const ll mod=998244353;
const int N=8000007;
int n,q,ndnum,root,a[N>>5],ls[N],rs[N];
ll sum[N],fla[N],flm[N];
templateinline void read(T &res){
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') flag=-1; res=ch-48;
    while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+ch-48; res*=flag;
}
int build(int l,int r){
    int p=++ndnum;
    if(l==r){
        sum[p]=0;
        flm[p]=1;
        return p;
    }
    int mid=(l+r)>>1;
    ls[p]=build(l,mid);
    rs[p]=build(mid+1,r);
    sum[p]=sum[ls[p]]+sum[rs[p]];
    flm[p]=1;
    return p;
}
void pushdown(int p,int l,int r){
    int mid=(l+r)>>1;
    sum[ls[p]]=(sum[ls[p]]*flm[p]%mod+(mid-l+1)*fla[p]%mod)%mod;
    sum[rs[p]]=(sum[rs[p]]*flm[p]%mod+(r-mid)*fla[p]%mod)%mod;
    fla[ls[p]]=(fla[ls[p]]*flm[p]%mod+fla[p])%mod;
    fla[rs[p]]=(fla[rs[p]]*flm[p]%mod+fla[p])%mod;
    flm[ls[p]]=flm[ls[p]]*flm[p]%mod;
    flm[rs[p]]=flm[rs[p]]*flm[p]%mod;
    fla[p]=0;flm[p]=1;
}
int xl,xr,yl,yr,x;
ll query(int l,int r,int p){
    if(xl<=l&&r<=xr) return sum[p];
    pushdown(p,l,r);
    int mid=(l+r)>>1;
    ll res=0;
    if(xl<=mid) res+=query(l,mid,ls[p]);
    if(xr>mid) res+=query(mid+1,r,rs[p]);
    return res%mod;
}
int sett(int l,int r,int p){
    if(p==0) p=++ndnum;
    if(yl<=l&&r<=yr){
        sum[p]=1;
        fla[p]=true;
        return p;
    }
    int mid=(l+r)>>1;
    if(yl<=mid){
        if(!ls[p]) ls[p]=++ndnum;
        ls[p]=sett(l,mid,ls[p]);
    }
    if(yr>mid){
        if(!rs[p]) rs[p]=++ndnum;
        rs[p]=sett(mid+1,r,rs[p]);
    }
    if(sum[ls[p]]==sum[rs[p]]) sum[p]=sum[ls[p]];
    else sum[p]=-1;
    return p;
}
int _get(int l,int r,int p){
    if(yl<=l&&r<=yr) return sum[p];
    int mid=(l+r)>>1;
    int la=100,ra=100;
    if(yl<=mid){
        if(!ls[p]) ls[p]=++ndnum;
        if(fla[p]==1) sum[ls[p]]=fla[ls[p]]=1;
        la=_get(l,mid,ls[p]);
    }
    if(yr>mid){
        if(!rs[p]) rs[p]=++ndnum;
        if(fla[p]==1) sum[rs[p]]=fla[rs[p]]=1;
        ra=_get(mid+1,r,rs[p]);
    }
    if(la==100) return ra;
    if(ra==100) return la;
    if(la==ra) return la;
    return -1;
}
void modify(int l,int r,int p,int c){
    if(xl<=l&&r<=xr){
        if(c==1){
            sum[p]=(sum[p]+r-l+1)%mod;
            fla[p]=(fla[p]+1)%mod;
            return;
        }
        if(c==2){
            sum[p]=sum[p]*2%mod;
            fla[p]=fla[p]*2%mod;
            flm[p]=flm[p]*2%mod;
            return;
        }
        yl=l,yr=r;
        int y=_get(1,n,x);
        if(y==0) c=1;
        else if(y==1) c=2;
        else c=0;
        if(c==1){
            sum[p]=(sum[p]+r-l+1)%mod;
            fla[p]=(fla[p]+1)%mod;
            return;
        }
        if(c==2){
            sum[p]=sum[p]*2%mod;
            fla[p]=fla[p]*2%mod;
            flm[p]=flm[p]*2%mod;
            return;
        }
    }
    pushdown(p,l,r);
    int mid=(l+r)>>1;
    if(xl<=mid) modify(l,mid,ls[p],c);
    if(xr>mid) modify(mid+1,r,rs[p],c);
    sum[p]=(sum[ls[p]]+sum[rs[p]])%mod;
}
int main()
{
    freopen("multiset.in","r",stdin);
    freopen("multiset.out","w",stdout);
    read(n);read(q);
    ndnum=n;
    root=build(1,n);
    while(q--){
        int opt;
        read(opt);
        if(opt==1){
            read(xl);read(xr);read(x);
            modify(1,n,root,0);
            yl=xl;yr=xr;
            sett(1,n,x);
        }
        else if(opt==2){
            read(xl);read(xr);
            printf("%lld\n",query(1,n,root));
        }
    }
//  while(q--){
//      int opt;
//      read(opt);
//      if(opt==1){
//          read(yl);read(yr);read(x);
//          sett(1,n,x);
//      }
//      else if(opt==2){
//          read(yl);read(yr);read(x);
//          printf("%d\n",_get(1,n,x));
//      }
//  }
    return 0;
}

T3

【题目描述】
小 X 同学觉得树上问题太毒瘤了,于是决定将树上的边删去,最终变成一个点。
现在有一个 n 个节点的树,他的游戏是这样的:

  1. 从剩下的所有边中等概率随机选中一条边 T。
  2. 将这条边删去,若这条边相连的两个点编号为 u 和 v,新建一个点 x,这个点与
    所有与 u 和 v 相邻的点有边,最后删去 u 和 v 及与它们相连的边,x 的编号等概率随
    机命名为 u 或 v。
    不断重复上述步骤,知道只剩下一个点。
    现在小 X 想知道,对于每个编号,最后剩下该编号的点的概率。
    树是一个没有环的连通图。
    【输入格式】
    从文件 tree.in 中读入数据。
    第一行一个整数 n。
    接下来 n 1 行,第 i 行两个整数 ui
    , vi,表示第 i 条边连接的两个点。

你可能感兴趣的:(CSP模拟赛20190922)