Codeforces Round 856 (Div. 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 ! b 1 ! ∗ b 2 ! ∗ . . . ∗ b k ! ∗ c 1 ! ∗ . . . ∗ c k ! \frac{n!}{b_1!*b_2!*...*b_k!*c_1!*...*c_k!} b1!b2!...bk!c1!...ck!n!,就是一个含有相同元素的全排列问题
可以看出其实 n ! b 1 ! ∗ b 2 ! ∗ . . . ∗ b k ! \frac{n!}{b_1!*b_2!*...*b_k!} b1!b2!...bk!n!其实是不变的,只要求所有的 1 c 1 ! ∗ . . . ∗ c k ! \frac{1}{c_1!*...*c_k!} c1!...ck!1和就行。到这应该能看出应该是用dp求所有的 1 c 1 ! ∗ . . . ∗ c k ! \frac{1}{c_1!*...*c_k!} c1!...ck!1和了,O( n 2 n^2 n2)的复杂度完全可以。

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

那么 f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] ∗ 1 ( c i − 1 ) ! + f [ i − 1 ] [ j ] ∗ 1 ( c i − 1 ) ! f[i][j]=f[i-1][j-1]*\frac{1}{(c_i-1)!}+f[i-1][j]*\frac{1}{(c_i-1)!} f[i][j]=f[i1][j1](ci1)!1+f[i1][j](ci1)!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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值