Codeforces Round 856 (Div. 2)(C~D)(D:线性筛的痛苦)

231 阅读2分钟

C. Scoring Subsequences

思路

只要看出所需要维护的长度一定是越来越长的就行,且每次只能+0 或者+1。所以每次只需要判断一下。 注意不用维护具体的分数,可能这个值会很大,导致爆掉

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#define fir(i,a,b) for(int i=a;i<b;i++)
#define dug cout<<"ceshi"<<endl;
typedef long long LL;
#define met(x,y) memset(x,y,sizeof x)
//#define read(x) scanf("%d",&x)
using namespace std;
typedef pair<int,int> PII;
const int N=1e5+10;
LL ss,xx;
int si=0;
LL a[N];
inline int read()
{
char c = getchar();int x = 0,s = 1;
while(c < '0' || c > '9') {if(c == '-') s = -1;c = getchar();}//是符号
while(c >= '0' && c <= '9') {x = x*10 + c -'0';c = getchar();}//是数字
return x*s;
}
bool cal (LL x,LL y,LL a,LL b)
{
   //cout<<x<<" "<<y<<" "<<a<<" "<<b;
   return x*b>=y*a;
}
int main()
{
//freopen("test.txt", "r", stdin);
int t=read();
while(t--)
{

   int n;
   cin>>n;
   si=1;
   cout<<1<<" ";
   for(int i=1;i<=n;i++)cin>>a[i];
   for(int i=2;i<=n;i++)
   {
      if(cal(a[i],si+1,a[i],a[i-si]))si++;
       cout<<si<<" ";
   }
   puts("");
}
//freopen("CON", "r", stdin);
//system("pause");
return 0;
}

D - Counting Factorizations

思路

首先必须要清楚几个点

  • 质数的种类一定要大于等于n
  • 在每一种方案中的底数中,每种质数只能出现一次

那么可以设一个c数组来存储每一种质数出现的次数,b数组来存储每一种非质数出现的次数 那么在每一次确定底数之后,如果该质数为底数就在c数组中减一 最终在这个底数方案中所有的答案为 n!b1!b2!...bk!c1!...ck!\frac{n!}{b_1!*b_2!*...*b_k!*c_1!*...*c_k!},就是一个含有相同元素的全排列问题 可以看出其实n!b1!b2!...bk!\frac{n!}{b_1!*b_2!*...*b_k!}其实是不变的,只要求所有的1c1!...ck!\frac{1}{c_1!*...*c_k!}和就行。到这应该能看出应该是用dp求所有的1c1!...ck!\frac{1}{c_1!*...*c_k!}和了,O(n2n^2)的复杂度完全可以。

设f[i][j]的状态表示为,在前i个数中,选了j个数为底数的和

那么 f[i][j]=f[i1][j1]1(ci1)!+f[i1][j]1(ci1)!f[i][j]=f[i-1][j-1]*\frac{1}{(c_i-1)!}+f[i-1][j]*\frac{1}{(c_i-1)!} 具体细节就看代码了

代码

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1e6+10,M=5100,Q=998244353;
long long f[M][M];
bool is_prime[N];//该数是不是质数
int prime[N],idx=0;
int prime_si[N],notprime_si[N];
vector<int> list_prime,notlist_prime;
int n;
long long fast[M],fast_inv[M];//阶乘和,阶乘和的逆
int qmi(long long x,int k)//快速幂
{
   long long res=1;
   while(k)
   {
       if(k&1)res=res*x%Q;
       x=x*x%Q;
       k>>=1;
   }
   return res;
}
void init()//筛质数
{
   is_prime[1]=true;
   for(int i=2;i<=(N-1);i++)
   {
       if(!is_prime[i])prime[idx++]=i;
       for(int j=0;prime[j]<=(N-1)/i;j++)
       {
           is_prime[prime[j]*i]=true;
           if(i%prime[j]==0)break;
       }
   }
   fast[0]=1;
   for(int i=1;i<M;i++)fast[i]=fast[i-1]*i%Q;
   for(int i=0;i<M;i++)fast_inv[i]=qmi(fast[i],Q-2);
}
bool check(int i,int j)
{
   if(i<j)return false;
   if(n-j>=list_prime.size()-i)return false;
   return true;
}
int main()
{
   init();
   cin>>n;
   list_prime.push_back(0);
   for(int i=1;i<=2*n;i++)
   {
       int tem;
       cin>>tem;
       if(is_prime[tem])
       {
           //cout<<tem;
           if(notprime_si[tem]==0)notlist_prime.push_back(tem);
           notprime_si[tem]++;;
       }
       else 
       {
           if(prime_si[tem]==0)list_prime.push_back(tem);
           prime_si[tem]++;
       }
   }
   f[0][0]=1;
   for(int i=1;i<list_prime.size();i++)
   {
       int tem=list_prime[i];
       for(int j=0;j<=n&&i>=j;j++)
       {
            if(check(i-1,j-1))f[i][j]=f[i-1][j-1]*fast_inv[prime_si[tem]-1]%Q;
           if(check(i-1,j))f[i][j]=(f[i][j]+f[i-1][j]*fast_inv[prime_si[tem]]%Q)%Q;
       }
   }
   long long res=f[list_prime.size()-1][n];
   for(auto ch:notlist_prime)
   {
       res=(res*fast_inv[notprime_si[ch]])%Q;
   }
   res=res*fast[n]%Q;
   cout<<res;
   return 0;
}

痛苦的回忆

当然不用线性筛,直接每次都判断也是可以的。 线性筛的话,两个循环一定是大于等于,不能是等于,不然会漏筛 但是我用了很多次都没出现这样的情况,所以调的半天代码,一直以为是dp那出错了可恶 错误的代码

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1e6+10,M=5100,Q=998244353;
long long f[M][M];
bool is_prime[N];
int prime[N],idx=0;
int prime_si[N],notprime_si[N];
vector<int> list_prime,notlist_prime;
int n;
long long fast[M],fast_inv[M];
int qmi(long long x,int k)
{
   long long res=1;
   while(k)
   {
       if(k&1)res=res*x%Q;
       x=x*x%Q;
       k>>=1;
   }
   return res;
}
void init()//筛质数
{
   is_prime[1]=true;
   for(int i=2;i<N;i++)
   {
       if(!is_prime[i])prime[idx++]=i;
       for(int j=0;prime[j]<N/i;j++)
       {
           is_prime[prime[j]*i]=true;
           if(i%prime[j]==0)break;
       }
   }
   fast[0]=1;
   for(int i=1;i<M;i++)fast[i]=fast[i-1]*i%Q;
   for(int i=0;i<M;i++)fast_inv[i]=qmi(fast[i],Q-2);
}
bool check(int i,int j)
{
   if(i<j)return false;
   if(n-j>=list_prime.size()-i)return false;
   return true;
}
int main()
{
   init();
   cin>>n;
   list_prime.push_back(0);
   for(int i=1;i<=2*n;i++)
   {
       int tem;
       cin>>tem;
       if(is_prime[tem])
       {
           //cout<<tem;
           if(notprime_si[tem]==0)notlist_prime.push_back(tem);
           notprime_si[tem]++;;
       }
       else 
       {
           if(prime_si[tem]==0)list_prime.push_back(tem);
           prime_si[tem]++;
       }
   }
   f[0][0]=1;
   for(int i=1;i<list_prime.size();i++)
   {
       int tem=list_prime[i];
       for(int j=0;j<=n&&i>=j;j++)
       {
            if(check(i-1,j-1))f[i][j]=f[i-1][j-1]*fast_inv[prime_si[tem]-1]%Q;
           if(check(i-1,j))f[i][j]=(f[i][j]+f[i-1][j]*fast_inv[prime_si[tem]]%Q)%Q;
       }
   }
   long long res=f[list_prime.size()-1][n];
   for(auto ch:notlist_prime)
   {
       res=(res*fast_inv[notprime_si[ch]])%Q;
   }
   res=res*fast[n]%Q;
   cout<<res;
   return 0;
}