JXOI2018簡要題解

JXOI2018簡要題解

T1 排序問題

題意

九條可憐是一個熱愛思考的女孩子。ios

九條可憐最近正在研究各類排序的性質,她發現了一種頗有趣的排序方法: Gobo sort !算法

Gobo sort 的算法描述大體以下:安全

  1. 假設咱們要對一個大小爲 \(n\) 的數列 \(a\) 排序。
  2. 等機率隨機生成一個大小爲 \(n\) 的排列 \(p\)
  3. 構造一個大小爲 \(n\) 的數列 \(b\) 知足 \(b_i=a_{p_i}\) ,檢查 \(b\) 是否有序,若是 \(b\) 已經有序了就結束算法,並返回 \(b\) ,否則返回步驟 \(2\)

顯然這個算法的指望時間複雜度是 \(O(n\times n!)\) 的,可是九條可憐驚奇的發現,利用量子的神奇性質,在量子系統中,能夠把這個算法的時間複雜度優化到線性。優化

九條可憐對這個排序算法進行了進一步研究,她發現若是一個序列知足一些性質,那麼 Gobo sort 會很快計算出正確的結果。爲了量化這個速度,她定義 Gobo sort 的執行輪數是步驟 \(2\) 的執行次數。spa

因而她就想到了這麼一個問題:code

如今有一個長度爲 \(n\) 的序列 \(x\) ,九條可憐會在這個序列後面加入 \(m\) 個元素,每一個元素是 \([l,r]\) 內的正整數。
她但願新的長度爲 \(n+m\) 的序列執行 Gobo sort 的指望執行輪數儘可能的多。她但願獲得這個最多的指望輪數。排序

九條可憐很聰明,她很快就算出了答案,她但願和你覈對一下,因爲這個指望輪數實在是太大了,因而她只要求你輸出對 \(998244353\) 取模的結果。遊戲

對於 \(30\%\) 的數據, \(T\leq 10\) , \(n,m,l,r\leq 8\)
對於 \(50\%\) 的數據, \(T\leq 300,n,m,l,r,a_i\leq 300\)
對於 \(60\%\) 的數據, \(\sum{r-l+1}\leq 10^7\)
對於 \(70\%\) 的數據, \(\sum{n} \leq 2\times 10^5\)
對於 \(90\%\) 的數據,\(m\leq 2\times 10^5\)
對於 \(100\%\) 的數據, \(T\leq 10^5,n\leq 2\times 10^5,m\leq 10^7,1\leq l\leq r\leq 10^9\)
對於 \(100\%\) 的數據, \(1\leq a_i\leq 10^9,\sum{n}\leq 2\times 10^6\)ci

題解

一道不錯的簽到題。it

首先若是每一個數都不一樣,畫一畫能夠知道,有且只有惟一的一種排列是知足條件的,此時須要\(n!\)次。

可是有數相同,那麼咱們能夠強制他們有大小關係、每一種大小關係對應一種排列。若是相同的數個數分別是\(a_1,a_2...a_k\),則答案爲\(\frac{n!}{a_1!a_2!a_3!...a_k!}\)

考慮怎麼樣加數會使得答案最大:確定越平均越大(優先加個數少的數字,由於乘的分母小)。因而就維護一個相似階梯的東西貪心地加就行了。

代碼

#include<iostream>
#include<algorithm>
using namespace std;
const int mod=998244353,N=2e5+10;
int n,m,l,r,jc[11000000],a[N],b[N],c[N],t[N];
int ksm(int x,int k)
{
    int s=1;for(;k;k>>=1,x=1ll*x*x%mod)
                if(k&1) s=1ll*s*x%mod;return s;
}
void Work()
{
    scanf("%d%d%d%d",&n,&m,&l,&r);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    int ans=jc[n+m],t1=0,t2=0,top=0;
    for(int i=1;i<=n;i++)
        if(a[i]>=l&&a[i]<=r) b[++t1]=a[i];
        else c[++t2]=a[i];
    b[t1+1]=c[t2+1]=0;
    for(int i=2,res=1;i<=t2+1;i++)
        if(c[i]!=c[i-1]) ans=1ll*ans*ksm(jc[res],mod-2)%mod,res=1;
        else res++;
    for(int i=2,res=1;i<=t1+1;i++)
        if(b[i]!=b[i-1]) t[++top]=res,res=1;
        else res++;
    sort(t+1,t+top+1);
    int s=r-l+1-top,nw=0,i=1;
    for(;i<=top;i++)
        if(1ll*s*(t[i]-nw)<=m)
            m-=1ll*s*(t[i]-nw),nw=t[i],s++;
        else break;
    nw+=m/s;m%=s;
    ans=1ll*ans*ksm(ksm(jc[nw+1],mod-2),m)%mod;
    ans=1ll*ans*ksm(ksm(jc[nw],mod-2),s-m)%mod;
    for(;i<=top;i++) ans=1ll*ans*ksm(jc[t[i]],mod-2)%mod;
    printf("%d\n",ans);
}
int main()
{
    int T;cin>>T;jc[0]=1;
    for(int i=1;i<=1e7+1e6;i++) jc[i]=1ll*jc[i-1]*i%mod;
    while(T--) Work();
}

T2 遊戲

題意

九條可憐是一個富有的女孩子。她長大之後創業了,開了一個公司。
可是管理公司是一個很累人的活,員工們常常揹着可憐偷懶,可憐須要時不時對辦公室進行檢查。

可憐公司有 \(n\) 個辦公室,辦公室編號是 \(l\sim l+n-1\) ,可憐會事先制定一個順序,按照這個順序依次檢查辦公室。一開始的時候,全部辦公室的員工都在偷懶,當她檢查完編號是 \(i\) 的辦公室時候,這個辦公室的員工會認真工做,而且這個辦公室的員工通知全部辦公室編號是 \(i\) 的倍數的辦公室,通知他們老闆來了,讓他們認真工做。所以,可憐檢查完第 \(i\) 個辦公室的時候,全部編號是 \(i\) 的倍數(包括 \(i\) )的辦公室的員工會認真工做。

可憐發現了員工們通風報信的行爲,她發現,對於每種不一樣的順序 \(p\) ,都存在一個最小的 \(t(p)\) ,使得可憐按照這個順序檢查完前 \(t(p)\) 個辦公室以後,全部的辦公室都會開始認真工做。她把這個 \(t(p)\) 定義爲 \(p\) 的檢查時間。

可憐想知道全部 \(t(p)\) 的和。

可是這個結果可能很大,她想知道和對 \(10^9+7\) 取模後的結果。

對於 \(20\%\) 的數據,\(r-l+1\leq 8\)
對於另 \(10\%\) 的數據,\(l=1\)
對於另 \(10\%\) 的數據,\(l=2\)
對於另 \(30\%\) 的數據,\(l\leq 200\)
對於 \(100\%\) 的數據,\(1\leq l\leq r\leq 10^7\)

題解

一道簡單的組合計數問題。

若是把倍數關係畫成一張拓撲圖的話,那麼當且僅當入度爲0的點都被選了,檢查結束。

設一共有k個入度爲0的點,考慮選多少次可以選全,答案就是

\[\sum_{i=k-1}^{n-1}(i+1)C_{i}^{k-1}k!(n-k)!\]

含義是在前\(i\)次中選了\(k-1\)個,在第\(i+1\)次中選了剩下的那一個。貢獻是\(i+1\),在前\(i\)次中選出\(k-1\)次決策用來選必選點,把每一種決策是否選必選點分配好後、只須要把必選點和非必選點排列上去就是方案了,爲\(k!(n-k)!\)

在找入度爲0的點是用線性篩,找到本身在\([l,r]\)且爲質數或最大因數不在\([l,r]\)的數。

代碼

#include<iostream>
using namespace std;
const int N=1e7+10,mod=1e9+7;
int l,r,n,Ans,cnt,ispri[N],pri[N],jc[N],inv[N],tot;
int ksm(int x,int k)
{
    int s=1;for(;k;k>>=1,x=1ll*x*x%mod)
                if(k&1) s=1ll*s*x%mod;return s;
}
void Pre()
{
    ispri[1]=pri[1]=1;
    if(l==1) {cnt=1;return;}
    for(int i=2;i<=r;i++)
    {
        if(!ispri[i]) pri[++tot]=i,cnt+=(i>=l);
        for(int j=1;j<=tot&&i*pri[j]<=r;j++)
        {
            ispri[i*pri[j]]=1;
            if(i<l&&i*pri[j]>=l) cnt++;
            if(i%pri[j]==0) break;
        }
    }
}
int main()
{
    cin>>l>>r;n=r-l+1;Pre();jc[0]=inv[0]=1;
    for(int i=1;i<=r;i++) jc[i]=1ll*jc[i-1]*i%mod;
    inv[r]=ksm(jc[r],mod-2);
    for(int i=r-1;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
    for(int i=cnt-1;i<=n-1;i++)
        (Ans+=1ll*jc[i]*inv[i-cnt+1]%mod*(i+1)%mod)%=mod;
    Ans=1ll*Ans*cnt%mod*jc[n-cnt]%mod;
    cout<<Ans<<endl;
}

T3 守衛

題意

九條可憐是一個熱愛運動的女孩子。

這一天她去登山,她的父親爲了她的安全,僱了一些保鏢,讓他們固定地呆在在山的某些位置,來實時監視九條可憐,從而保護她。

具體來講,一座山能夠描述爲一條折線,折線的下方是岩石。這條折線有 \(n\) 個折點,每一個折點上有一個亭子,第 \(i\) 個折點的座標是 \((i,h_i)\)九條可憐只可能會在亭子處玩耍,那些保鏢也只會在亭子處監視可憐。

因爲技術方面的緣由,一個保鏢只能監視全部他能看獲得的,橫座標不超過他所在位置的亭子。咱們稱一個保鏢能看到一個亭子 \(p\) ,當且僅當他所在的亭子 \(q\)\(p\) 的連線不通過任何一塊岩石。特別地,若是這條連線剛好通過了除了 \(p,q\) 之外的亭子,那麼咱們認爲保鏢看不到可憐。

僱傭保鏢是一件很費錢的事情,可憐的父親但願保鏢越少越好。

可憐的父親還但願獲得詳盡的僱傭保鏢的方案,他知道有些亭子可能正在維修,他想對全部的 \(1\leq l\leq r\leq n\) 計算:若是事先已知了只有區間 \([l,r]\) 的亭子能夠用來玩耍(和監視),那麼最少須要多少個保鏢,才能讓 \([l,r]\) 中的每個亭子都被監視到。

可憐的父親已經獲得了一個結果,他但願和你覈實他的結果是否正確。

對於 \(30\%\) 的數據, \(n\leq 20\)
對於 \(70\%\) 的數據, \(n\leq 500\)
對於 \(100\%\) 的數據, \(n\leq 5000\)
對於 \(100\%\) 的數據, \(1\leq h_i\leq 10^9\)

題解

略有難度的DP。

首先能夠維護斜率最值來獲得兩點間的可見關係。(一開始覺得可見區域必定是一段區間,搞了很久的貪心結果錯了,Hack:5 1 4 5)

而後設\(f[l][r]\)表示這個區間的最少關鍵點數,那麼必定要在\(r\)處放置一個,再依次找\(r\)看不到的極大區間\([l1,r1]\),考慮在\(r1,r1+1\)中選一個點做爲關鍵點就行了。

一個須要解釋的地方就是爲何\([r1+2,r]\)都看不到\([l1,r1]\):由於\(r\)看獲得\(r1+1\)\(r1+2\),因此\(r\)\(r1+2\)連線的夾角要大一些、在\(r\)\(r1+1\)連線下方;若是\(r1+2\)看獲得\(r1\)的話,則\(r1+2\)\(r1+1\)的連線的夾角比\(r1+2\)\(r1\)的大。夾角均爲x正半軸出發的有向角,如此一畫發現矛盾,\(r1+2\)看不到\(r1\)

代碼很簡單,固定一個\(r\),從後往前掃\(l\)

代碼

#include<iostream>
using namespace std;
const int N=5100;
int n,h[N],f[N][N],see[N][N],ans;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>h[i];
    for(int i=1,l=0;i<=n;i++,l=i-1)
        for(double mn=1e9;l>=1;l--)
            if(1.0*(h[i]-h[l])/(i-l)<mn) mn=1.0*(h[i]-h[l])/(i-l),see[l][i]=1;
    for(int r=1;r<=n;r++)
        for(int l=r,s=1,lst=l;l>=1;l--)
        {
            if(see[l][r])
            {
                if(!see[l+1][r]) s+=min(f[l+1][lst],f[l+1][lst+1]);
                f[l][r]=s;
            }
            else
            {
                if(see[l+1][r]) lst=l;
                f[l][r]=s+min(f[l][lst],f[l][lst+1]);
            }
            ans^=f[l][r];
        }
    cout<<ans<<endl;
}

後記

據說JXOI 80分就能進隊什麼鬼啊。。

這放在HN那不曉得有多少AK的。。

我作起來仍是比較輕鬆吧,100+100+30。固然T2推式子的時候有一項推錯了,瞟一眼題解發現這題確實是推式子才繼續把它推對;T3xjb貪心過了前三個點。這些都是考場上三個半小時不必定能寫出來的,因此說雖然80分能夠進隊,但真正若是是考試,本身又能不能不失誤呢?還須要斟酌、修煉。

相關文章
相關標籤/搜索