一道好玩的組合數學的推公式題(綠名題, 1879C - Make it Alternating

月下~观星發表於2024-04-06

1879C - Make it Alternating

先貼程式碼,看能不能理解

str a;
ll v[N];//裝著01化為-1,1的數的陣列
ll f[N];//裝著預處理的組合數
void moon(){
  cin>>a;
  n=0;
  m=a.size();
  eps(i,0,m+10)v[i]=0; //eps()是一個陋習,define 定義的for迴圈
  for(auto p:a){
 v[++n]=(p=='1'?1:-1);
  }
  ll res=v[1],ans=0,nres=0;
  multiset<ll>s;//計數,比較方便
  eps(i,2,n)
  {
    while(res==v[i]){
        ans++;i++;//因為記住的不包括自己,所以後面是(p+1)
    }
    nres+=ans;
    if(ans>0)
    s.insert(ans);
    ans=0;
    res=v[i];
  }
    cout<<nres<<' ';
    res=1;
    for(auto p:s){
   //   if(p+1==nres)res=res%mod+1;
      res=res%mod*(p+1)%mod;  //所有可消除元素的連乘法,是一個乘法原理
    }
    res=res%mod*f[(nres)]%mod;//乘以取出來的數的階乘,用來排位置
    cout<<res%mod<<endl;  
}
int main()
{
  icc(); 
  f[0]=1;
  eps(i,1,N)
f[i]=i%mod*f[i-1]%mod;
 cin>>_;
  while(_--)
  moon();
  return 0;
}

思想分析:

題目先要求的是最小操作次數

很容易貪心貢獻的想到,常見的標 -1,1 (當然這個題沒用

我們要交替,也就是連續不相等,那麼連續相等的元素我們只會保留一個

所以用了一個multiset裝著每一次去掉的數,最後輸出res(和)

重點的是接下來的東西

我們怎麼計算多少種操作方法,顯然每個連續的數我們都可以取走(ans-1)個留下一個,也就是一個簡單的乘法原理了,ans1 * ans2 * ans3 * ...嗎?

100010 中間三個0是我們要清理的,顯然是有順序的,我們可以先拿左邊兩個,到處拿取,看看樣例,它的拿取是有順序的!

所以我們拿出元素後我們還得排序,分析拿取順序,乘以(k)! (k是拿出元素的總和)

那麼公式就出現了 ∏(1,s.size())len * (k)! (那個特殊字元是連乘)

相關文章