1. 程式人生 > >[jzoj NOIP2018模擬10.23]

[jzoj NOIP2018模擬10.23]

lzo 二維 jdom 題意 getchar() show 時間復雜度 esc nlb

丟分主要是下面幾個方面:

1.T2代碼交錯了,有個特判沒寫丟了10分

2.T1線段樹加等差數列寫錯了(其實二維差分就可以,但我當時不會)

3.T3思考再三還是為了10分寫上了主席樹,還是寫錯了

總體評價就是和正解沒什麽聯系,暴力也沒寫滿


T1:sequence

題目鏈接:

http://172.16.0.132/senior/#contest/show/2535/0

題目:

小 F 是一位 Hack 國的居民,他生活在一條長度為 n 的街道上,這個街道上總共有 n 個商店。每個商店裏售賣著不同的 Hack 技能包,每個商店本身也會有個便利值。初始時,每個商店的便利值均為 0。每一天,街道上都會有一些商店優化改造。


具體來說,對於每一天,優化改造的商店都是一個連續的區間 l ∼ r,每次優化改造也會有一個優化參數 k。對於所有 l ≤ i ≤ r ,第 i 個商店的便利值會增加
技術分享圖片

小 F 想知道,m 天之後,每個商店的便利值分別是多少。由於小 F 並不喜歡高精度,因此你只需要輸出便利值對 10^9 + 7 取模的結果。

題解:

考慮一個下標從 0 開始的數列,這個數列的每個數均為 1。 我們對這個數列做$ k$ 階前綴和,得到的數列第i項的值為$\dbinom{k+i}{k}$。

於是我們發現題目中的這個加法實際上就是對當前的序列的第$k+1$階差分(考慮到影響0階前綴和的是1階差分序列,影響$k$階前綴和的就是$k+1$階差分序列),設第$k$階差分數組為$cha[k]$,那麽題目中的這個加法產生的效果就是$cha[k+1][l]++,cha[k+1][r+1]--$

但如果是這樣的話還是不行的,我們還需要對$1~k$階差分序列的第$r+1$項做一些運算,設當前為第i階差分序列(1<=i<=k),在第r+1位減去$\dbinom{r-l+k-(i-1)}{k-(i-1)}$,即減去當前差分序列的前綴和,為的是消去對後面的影響

可以手推一下樣例方便理解

技術分享圖片
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;

const int N=5e5+15; const int mo=1e9+7; int n,m; ll cha[25][N],s[25][N],ans[N],C[N][25]; inline int read(){ char ch=getchar();int s=0,f=1; while (ch<0||ch>9) {if (ch==-) f=-1;ch=getchar();} while (ch>=0&&ch<=9) {s=(s<<3)+(s<<1)+ch-0;ch=getchar();} return s*f; } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); C[0][0]=1; for (int i=1;i<N;i++) for (int j=0;j<25;j++) { if (!j) C[i][j]=1; else C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo; } n=read();m=read(); while (m--) { int l=read(),r=read(),k=read(); cha[k+1][l]++;cha[k+1][r+1]--; for (int i=k;i;i--) { cha[i][r+1]=(cha[i][r+1]+mo-C[k-(i-1)+r-l][k-(i-1)])%mo; } } for (int i=20;i>=0;i--) { for (int j=1;j<=n;j++) s[i][j]=(cha[i+1][j]+s[i][j-1]+mo)%mo; for (int j=1;j<=n;j++) cha[i][j]=(cha[i][j]+mo+s[i][j]+mo)%mo; } for (int i=1;i<=n;i++) printf("%lld\n",cha[0][i]); return 0; }
View Code

T2:Bomb

題目鏈接:

http://172.16.0.132/senior/#contest/show/2535/1

題目:

常數國與 Hack 國近年來戰火紛飛。
常數國共有 n 個城市,每兩個城市之間均有一條交通線聯通。如今常數國遭到 Hack 國的重創,岌岌可危。Hack 國國王小 K 決定轟炸常數國的交通線,對常數國發起最後的攻擊。
面對危難之時,常數國國王決定更換首都。在 Hack 國的轟炸結束之後,常數國的領土將會分成若幹個聯通塊。常數國的首都,將會從聯通塊大小最大的聯通塊中,隨機選擇一個城市,作為首都。
小 K 得知了常數國的應對方案之後,他想知道,Hack 國有多少種不同的轟炸方案,使得常數首都所在的聯通塊大小恰好為 k。兩種轟炸方案是不同的,當且僅當一條交通線在一種方案中存在,在另一種方案中被轟炸。由於方案數可能很大,你需要輸出方案數對 998,244,353 取模的結果。

題意:

詢問n個有編號點形成的最大連通塊為k的方案數

首先我們要知道這麽一個式子,設$f_n$為$n$個有編號節點的連通圖的方案數,$f_i=2^{\dbinom{i}{2}}-\sum_{j=1}^{n-1}2^{\dbinom{i-j}{2}}\dbinom{i-1}{j-1} f_j$

現在回到題目,設$p_i$表示i個有標號節點的最大連通塊等於k的連通圖的個數,$p_n$即為答案;設$q_i$表示i個有標號節點的最大連通塊小於等於k的連通圖的個數

考慮初始化:$p_0=0,q_0=1$

考慮轉移:$\sum_{j=1}^{min(i,k)}$

\begin{equation}
\left\{
\begin{array}{lr}
p_i+=p_{i-j}\times \dbinom{i-1}{j-1} f_j, j<k&\\
p_i+=q_{i-j}\times \dbinom{i-1}{j-1}f_j, j=k&
\\q_i+=q_{i-j}\times \dbinom{i-1}{j-1}f_j&
\end{array}
\right.
\end{equation}

技術分享圖片
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;

const int N=2e3+15;
const int mo=998244353;
int n,k;
ll C[N][N],f[N],p[N],q[N];
ll qpow(ll a,ll b)
{
    ll re=1;
    for (;b;b>>=1,a=a*a%mo) if (b&1) re=re*a%mo;
    return re;
}
int main()
{
    freopen("bomb.in","r",stdin);
    freopen("bomb.out","w",stdout);
    scanf("%d%d",&n,&k);
    C[0][0]=1;
    for (int i=1;i<=n;i++)
        for (int j=0;j<=i;j++)
        {
            if (!j) C[i][j]=1;
            else C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
        }
    f[0]=1;f[1]=1;
    for (int i=2;i<=n;i++)
    {
        ll re=qpow(2,C[i][2]);
        for (int j=1;j<i;j++) re=(re+mo-qpow(2,C[i-j][2])*C[i-1][j-1]%mo*f[j]%mo)%mo;
        f[i]=re;
    }
    q[0]=1;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=min(i,k);j++)
        {
            if (j<k)
            {
                p[i]=(p[i]+p[i-j]*C[i-1][j-1]%mo*f[j]%mo)%mo;
            }
            if (j==k)
            {
                p[i]=(p[i]+q[i-j]*C[i-1][j-1]%mo*f[j]%mo)%mo;
            }
            q[i]=(q[i]+q[i-j]*C[i-1][j-1]%mo*f[j]%mo)%mo;
        }
    printf("%lld\n",p[n]);
    return 0;
}
View Code

T3:queue

題目鏈接:

http://172.16.0.132/senior/#contest/show/2535/2

題目:

技術分享圖片

題解:

考慮分塊,塊內元素我們用鏈表相連,維護每個快內每個元素出現的個數。顯然對於詢問我們可以整塊詢問,邊界暴力

考慮輪轉操作,我們把r所在元素從r所在連通塊刪去,然後加入到l之前的位置。設l所在連通塊為k1,r所在連通塊為k2,讓k1到k2-1每個連通塊都把最後一個元素丟給後面一個連通塊,這樣我們就保證了連通塊的大小不變了

時間復雜度顯然是帶根號的

deque實現鏈表方便的多

技術分享圖片
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<deque>
#include<cmath>
using namespace std;

const int N=1e5+15;
int n,m;
int qu[500][N],belong[N],st[N],ed[N],a[N];
deque <int> bl[N];
inline int read(){
    char ch=getchar();int s=0,f=1;
    while (ch<0||ch>9) {if (ch==-) f=-1;ch=getchar();}
    while (ch>=0&&ch<=9) {s=(s<<3)+(s<<1)+ch-0;ch=getchar();}
    return s*f;
}
int main(){
    freopen("queue.in","r",stdin);
    freopen("queue.out","w",stdout);
    n=read();m=read();
    if (!n&&!m) return 0;
    for (int i=1;i<=n;i++) a[i]=read();
    int blo=sqrt(n);
    for (int i=1;i<=n;i++) belong[i]=(i-1)/blo+1;
    for (int i=1;i<=n;i++) bl[belong[i]].push_back(a[i]),qu[belong[i]][a[i]]++;
    for (int i=1;i<=(n-1)/blo+1;i++) 
    {
        st[i]=(i-1)*blo+1;ed[i]=min(i*blo,n);
    }
    while (m--)
    {
        int opt=read();
        if (opt==1)
        {
            int l=read(),r=read(),k1=belong[l],k2=belong[r];
            int k=bl[k2][r-st[k2]];
            bl[k2].erase(bl[k2].begin()+r-st[k2]);
            qu[k2][k]--;
            bl[k1].insert(bl[k1].begin()+l-st[k1],k);
            qu[k1][k]++;
            if (k1==k2) continue;
            for (int i=k1;i<k2;i++)
            {
                k=bl[i].back();
                bl[i].erase(bl[i].end()-1);
                qu[i][k]--;
                bl[i+1].push_front(k);
                qu[i+1][k]++;
            }
        }
        if (opt==2)
        {
            int l=read(),r=read(),k=read(),k1=belong[l],k2=belong[r];
            int re=0;
            if (k1==k2)
            {
                for (int i=l-st[k1];i<=r-st[k1];i++) if (bl[k1][i]==k) ++re;
                continue;
            }
            for (int i=l-st[k1];i<=ed[k1]-st[k1];i++) 
            {
                if (bl[k1][i]==k) ++re;
            }
            for (int i=0;i<=r-st[k2];i++) if (bl[k2][i]==k) ++re;
            if (k1+1<=k2-1)
            {
                for (int i=k1+1;i<k2;i++) re+=qu[i][k];
            }
            printf("%d\n",re);
        }
    }
    return 0;
}
View Code

[jzoj NOIP2018模擬10.23]