CSP模擬賽20190922

目錄c++

@(CSP模擬賽20190922)
兩天的一二題都挺水的,指望原本能夠得400,可是沒有一天是得完了能得的分的。ui

DAY1

T1

優美的字符串(string)
【題目描述】
小 Y 送給小 F 一個字符串做爲禮物,這個字符串只由’a’ 和’b’ 組成。
因爲小 F 患有嚴重的強迫症,他以爲這個字符串並不優美,他決定對它作一些
操做:
每次操做從字符串中選擇一個’ab’ 子串,並將其替換爲’bba’。
若是一個字符串的全部’b’ 都在全部’a’ 前面,他認爲這個字符串是優美的。
如今小 F 想知道,最少須要多少次操做,能使這個字符串是優美的,或者這個字
符串不可能變成優美的。
【輸入格式】
從文件 string.in 中讀入數據。
一行一個只由’a’ 和’b’ 組成的字符串。
【輸出格式】
輸出到文件 string.out 中。
輸出一行一個整數,若是無解,輸出「-1」。不然輸出最少操做次數對.
1000000007
取. 模. 。spa

大水題,手推五分鐘就切了。code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1000007;
const ll mod=1000000007;
ll dis[N];
char c[N];
template<class T>inline 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的狀況ci

#include<bits/stdc++.h>
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;i<n;++i)
        if(num[i]==1){
            res=(res+C(x-cnt,n-i-1))%mod;
            ++cnt;
        }
    if(cnt==x) res=(res+1)%mod;
    return res;
}
void dfs(int onenum,int stp){
    if(stp==k){
        ans=(ans+cal(onenum))%mod;
        return;
    }
    if(onenum>lim) 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<n;++i) num[i]=ch[i]-'0';
    lim=log(n)/log(2)+1;
    for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
    dfs(1,1);
    if(k==1) --ans;
    printf("%lld\n",ans);
    return 0;
}

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<y,距離爲depx+depy+(sk-(sy-sx))=sk+(depx+sx)+(depy-sy)。
咱們須要讓三種狀況的最大值最小。考慮求每種狀況的最大值:
預處理depi-si,depi+si,維護前綴和後綴最大值,就能獲得每種狀況的最大值,記錄全部最大值的最小值,就是答案。get

反正我不會string

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 中。
輸出一行一個整數。表示不能緩解交通壓力的方案數。it

bfs求任意兩點間的最短路,而後暴力枚舉任意兩點連邊是否可使最短路變短(相似dijsktra和floyed的鬆弛操做)

#include<bits/stdc++.h>
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;
}
template<class T>inline 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<int> 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<cstdio>
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];
template<class T>inline 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 條邊鏈接的兩個點。
相關文章
相關標籤/搜索