「BJOI2019」

#4372. 「BJOI2019」排兵佈陣

題目描述:

小 C 正在玩一款排兵佈陣的遊戲。在遊戲中有 $n$ 座城堡,每局對戰由兩名玩家來爭奪這些城堡。每名玩家有 $m$ 名士兵,能夠向第 $i$ 座城堡派遣 $a_i$ 名士兵去爭奪這個城堡,使得總士兵數不超過 $m$。node

若是一名玩家向第 $i$ 座城堡派遣的士兵數嚴格大於對手派遣士兵數的兩倍,那麼這名玩家就佔領了這座城堡,得到 $i$ 分。c++

如今小 C 即將和其餘 $s$ 名玩家兩兩對戰,這 $s$ 場對決的派遣士兵方案必須相同。小 C 經過某些途徑得知了其餘 $s$ 名玩家即將使用的策略,他想知道他應該使用什麼策略來最大化本身的總分。git

因爲答案可能不惟一,你只須要輸出小 C 總分的最大值。ide

 思路:

揹包問題ui

$f_{i,j}$ 表示選到前 $i$ 座城堡已經用了 $j$ 個士兵所能得到總分的最大值。spa

假若直接枚舉每座城堡派幾個士兵效率是 $O(nm^{2})$ ,可是咱們發現只有多獲勝一我的才能使總分增多,因此咱們能夠考慮枚舉每座城堡贏幾我的,效率變成 $O(nms)$ 。3d

 代碼:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=105,M=2e4+5;
int n,s,m,a[N][N],f[M];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
int main()
{
    s=read();n=read();m=read();
    for(int i=1;i<=s;i++)for(int j=1;j<=n;j++)a[j][i]=read()*2+1;
    for(int i=1;i<=n;i++)sort(a[i]+1,a[i]+1+s);
    for(int i=1;i<=n;i++)for(int j=m-1;j>=0;j--){
        for(int k=1;k<=s;k++){
            if(a[i][k]+j>m)break;
            f[a[i][k]+j]=max(f[a[i][k]+j],f[j]+k*i);
        }
    }
    int ans=0;
    for(int i=1;i<=m;i++)ans=max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}
View Code

 

 

#4374. 「BJOI2019」光線

題目描述:

當一束光打到一層玻璃上時,有必定比例的光會穿過這層玻璃,必定比例的光會被反射回去,剩下的光被玻璃吸取。code

設對於任意 $x$,有 $x\times a_i\%$ 單位的光會穿過它,有 $x\times b_i\%$ 的會被反射回去。blog

如今 $n$ 層玻璃疊在一塊兒,有 $1$ 單位的光打到第 $1$ 層玻璃上,那麼有多少單位的光能穿過全部 $n$ 層玻璃呢?遊戲

 思路:

$dp$ 問題

令 $f_{i}$ 表示 $1$ 單位光射入第 $i$ 層玻璃,能有多少穿過 $n$ 層玻璃。

令 $g_{i}$ 表示 $1$ 單位的光射入第 $i$ 層玻璃,最終能有多少光反射出去。
$$
f_{i}=a_i*(f_{i+1}+g_{i+1}*b_{i}*f_{i+1}+...)
$$

$$
f_{i}=a_i*f_{i+1}*\frac{1}{1-b_i*g_{i+1}}
$$

$$
gi=b_i+a_i*g_{i+1}*a_{i}+a_i*g_{i+1}*b_{i}*g_{i+1}*a_{i}...
$$

$$
g_{i}=b_i+a_i^2*g_{i+1}*\frac{1}{1-b_i*g_{i+1}}
$$

 代碼:

#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=5e5+5,p=1e9+7,ny=570000004;
int n,a[N],b[N],f[N],g[N];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il int ksm(LL a,int y){
    LL b=1;
    while(y){
        if(y&1)b=b*a%p;
        a=a*a%p;y>>=1;
    }
    return b;
}
il int mu(int x,int y){
    return (x+y>=p)?x+y-p:x+y;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=1ll*read()*ny%p;b[i]=1ll*read()*ny%p;
    }
    f[n]=a[n];g[n]=b[n];
    for(int i=n-1;i;i--){
        f[i]=1ll*a[i]*f[i+1]%p*ksm(mu(1,p-1ll*g[i+1]*b[i]%p),p-2)%p;
        g[i]=mu(b[i],1ll*a[i]*a[i]%p*g[i+1]%p*ksm(mu(1,p-1ll*b[i]*g[i+1]%p),p-2)%p);
    }
    printf("%d\n",f[1]);
    return 0;
}
View Code

 

#4375. 「BJOI2019」刪數

題目描述:

對於任意一個數列,若是能在有限次進行下列刪數操做後將其刪爲空數列,則稱這個數列能夠刪空。一次刪數操做定義以下:

- 記當前數列長度爲 $k$,則刪掉數列中全部等於 $k$ 的數。

現有一個長度爲 $n$ 的數列 $a$,有 $m$ 次修改操做,第 $i$ 次修改後你要回答:通過 $i$ 次修改後的數列 $a$,至少還須要修改幾個數纔可刪空?

每次修改操做爲單點修改或數列總體加一或數列總體減一。

思路:

對於一個數 $x$ 共有 $b[x]$ 個,那麼他所能覆蓋的區間爲 $[x-b[x]+1,x]$ ,答案即爲區間內沒有被覆蓋的數。

對於總體加減至關於把被覆蓋區間範圍總體移動。

能夠用線段樹維護。

 代碼:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=6e5+5;
int n,m,a[N],ans,mx,b[N],fir,mn[N<<2],num[N<<2],tag[N<<2];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il void update(int x){
    mn[x]=min(mn[x<<1],mn[x<<1|1]);
    num[x]=(mn[x]==mn[x<<1]?num[x<<1]:0)+(mn[x]==mn[x<<1|1]?num[x<<1|1]:0);
}
il void pushdown(int x){
    if(!tag[x])return;
    int v=tag[x];tag[x]=0;
    mn[x<<1]+=v;mn[x<<1|1]+=v;tag[x<<1]+=v;tag[x<<1|1]+=v;
}
il void build(int x,int l,int r){
    num[x]=r-l+1;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
}
il void change(int x,int l,int r,int ql,int qr,int v){
    if(ql<=l&&r<=qr){
        tag[x]+=v;mn[x]+=v;
        return;
    }
    int mid=(l+r)>>1;pushdown(x);
    if(ql<=mid)change(x<<1,l,mid,ql,qr,v);
    if(mid<qr)change(x<<1|1,mid+1,r,ql,qr,v);
    update(x);
}
il int query(int x,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){
        if(mn[x]==0)return num[x];
        else return 0;
    }
    int res=0,mid=(l+r)>>1;pushdown(x);
    if(ql<=mid)res=query(x<<1,l,mid,ql,qr);
    if(mid<qr)res+=query(x<<1|1,mid+1,r,ql,qr);
    return res;
}
int main()
{
    n=read();m=read();
    mx=2*(n+m);fir=n+m;build(1,1,mx);
    for(int i=1;i<=n;i++)a[i]=read()+fir,b[a[i]]++;
    for(int i=fir+1;i<=fir+n;i++)if(b[i])change(1,1,mx,i-b[i]+1,i,1);
    for(int i=1;i<=m;i++){
        int p=read(),x=read();
        if(!p){
            if(x==1){
                if(b[fir+n])change(1,1,mx,fir+n-b[fir+n]+1,fir+n,-1);
                fir--;
                if(b[fir+1])change(1,1,mx,fir+1-b[fir+1]+1,fir+1,1);
            }
            else{
                if(b[fir+1])change(1,1,mx,fir+1-b[fir+1]+1,fir+1,-1);
                fir++;
                if(b[fir+n])change(1,1,mx,fir+n-b[fir+n]+1,fir+n,1);
            }
        }
        else{
            int now=a[p];
            if(now<=fir+n&&now>fir)change(1,1,mx,now-b[now]+1,now-b[now]+1,-1);b[now]--;
            now=fir+x;b[now]++;a[p]=now;
            if(now<=fir+n&&now>fir)change(1,1,mx,now-b[now]+1,now-b[now]+1,1);
        }
        printf("%d\n",query(1,1,mx,fir+1,fir+n));
    }
    return 0;
}
View Code

 

#4369. 「BJOI2019」奧術神杖

題目:

Bezorath 大陸抵抗地災軍團入侵的戰爭進入了僵持的階段,世世代代生活在 Bezorath 這片大陸的精靈們開始尋找遠古時代諸神遺留的神器,試圖藉助神器的神祕力量幫助她們打敗地災軍團。

在付出了慘痛的代價後,精靈們從步步兇險的遠古戰場取回了一件保存尚無缺的神杖。但在經歷過那場全部史書都視爲禁忌的「諸神黃昏之戰」後,神杖上鑲嵌的奧術寶石已經殘缺,神力也幾乎消耗殆盡。精靈高層在至高會議中決定以舉國之力收集殘存至今的奧術寶石,並重金懸賞天下能工巧匠修復這件神杖。

你做爲神術一脈第五百零一位傳人,接受了這個艱鉅而神聖的使命。 神杖上從左到右鑲嵌了 $n$ 顆奧術寶石,奧術寶石一共有 $10$ 種,用數字 `0123456789` 表示。有些位置的寶石已經殘缺,用 `.` 表示,你須要用無缺的奧術寶石填補每一處殘缺的部分(每種奧術寶石個數不限,且不可以更換未殘缺的寶石)。古老的魔法書上記載了 $m$ 種咒語 $(S_i,V_i)$,其中 $S_i$ 是一個非空數字串,$V_i$ 是這種組合可以激發的神力。

神杖的初始神力值 $\mathrm{Magic} = 1$,每當神杖中出現了連續一段寶石與 $S_i$ 相等時,神力值 $\mathrm{Magic}$ 就會乘以 $V_i$。但神杖若是包含了太多咒語就再也不純淨致使神力下降:設 $c$ 爲神杖包含的咒語個數(若咒語類別相同但出現位置不一樣視爲屢次),神杖最終的神力值爲 $\sqrt[c]{\mathrm{Magic}}$。(若 $c = 0$ 則神杖最終神力值爲 $1$。)

例若有兩種咒語 $(01,3)$ 、$(10,4)$,那麼神杖 `0101` 的神力值爲 $\sqrt[3]{ 3 \times 4 \times 3}$。

你須要使修復好的神杖的最終的神力值最大,輸出任何一個解便可。

思路:

第一部的轉換仍是挺妙妙的
$$
val=\sqrt[c]{\prod_{i=1}^{c} V_{t_i}}
$$
若是對權值取對數
$$
ln^{val}=\frac{\sum_{i=1}^{c}V_{ti}}{c}
$$
這就是典型的分數規劃了

至少在 $ac$ 自動機上跑 $dp$ 。效率是 $O(sn)$ 。

 代碼:

#include<bits/stdc++.h>
#define il inline
#define db double
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1505;
const db eps=1e-7;
int n,m,ch[N][11],cnt=1,num[N],fa[N];
db val[N],eg[N],f[N][N];
char s[N],t[N],res;
struct node{
    int x,y,tp;
}fr[N][N];
il int read(){
   int x,f=1;char ch;
   _(!)ch=='-'?f=-1:f;x=ch^48;
   _()x=(x<<1)+(x<<3)+(ch^48);
   return f*x;
}
il void insert(db v){
    int x=1,l=strlen(t+1);
    for(int i=1;i<=l;i++){
        if(!ch[x][t[i]-'0'])ch[x][t[i]-'0']=++cnt;
        x=ch[x][t[i]-'0'];
    }
    num[x]++;val[x]+=v;
}
il void getfail(){
    queue<int> q;fa[1]=1;
    for(int i=0;i<10;i++){
        if(!ch[1][i])ch[1][i]=1;
        else q.push(ch[1][i]),fa[ch[1][i]]=1;
    }
    while(!q.empty()){
        int x=q.front();q.pop();
        val[x]+=val[fa[x]];num[x]+=num[fa[x]];
        for(int i=0;i<10;i++){
            if(!ch[x][i])ch[x][i]=ch[fa[x]][i];
            else{
                fa[ch[x][i]]=ch[fa[x]][i];
                q.push(ch[x][i]);
            }
        }
    }
}
il bool pd(db m){
    for(int i=1;i<=cnt;i++)eg[i]=m*num[i]-val[i];
    for(int i=0;i<=n;i++)for(int j=1;j<=cnt;j++)f[i][j]=1e9;
    f[0][1]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=cnt;j++){
            if(f[i-1][j]>1e6)continue;
            if(s[i]=='.'){
                for(int k=0;k<10;k++){
                    if(f[i-1][j]+eg[ch[j][k]]<f[i][ch[j][k]]){
                        f[i][ch[j][k]]=f[i-1][j]+eg[ch[j][k]];
                        fr[i][ch[j][k]]=(node){i-1,j,k};
                    }
                }
            }
            else{
                int k=s[i]-'0';
                if(f[i-1][j]+eg[ch[j][k]]<f[i][ch[j][k]]){
                    f[i][ch[j][k]]=f[i-1][j]+eg[ch[j][k]];
                    fr[i][ch[j][k]]=(node){i-1,j,k};
                }
            }
        }
    }
    for(int i=1;i<=cnt;i++)if(f[n][i]<0)return 1;
    return 0;
}
il void back(int x,int y){
    if(!x)return;
    back(x-1,fr[x][y].y);
    printf("%c",fr[x][y].tp+'0');
}
int main()
{
    n=read();m=read();
    scanf(" %s",s+1);
    for(int i=1;i<=m;i++){
        scanf(" %s",t+1);
        db x=read();x=log2(x);
        insert(x);
    }
    getfail();
    db l=0,r=100;
    while(r-l>eps){
        db mid=(l+r)/2.0;
        if(pd(mid))l=mid;
        else r=mid;
    }
    pd(l);//cout<<l<<endl;
    for(int i=1;i<=cnt;i++)if(f[n][i]<0){
        back(n,i);break;
    }
    puts("");
    return 0;
}
View Code
相關文章
相關標籤/搜索