2018-nowcoder-9

NowCoder 五一贺题记录

A.Circulant Matrix(FWT)

Problem

给定一个长度为\(n\)的数组\(\{a\}\),下标从\(0\)开始,定义\(n\times n\)矩阵\(A\)\(A[i][j]=a_{i\bigoplus j}\),定义数组\(b=Ax(mod\ P)\),其中\(P=10^9+7\)\(x\)\(n\times 1\)的列向量。现在给定\(a\)\(b\),求出列向量\(x\),保证唯一。

Sol

\(\sum_{j=1}^nA_{ij}x_{j}=\sum_{j=1}^na_{i\bigoplus j}x_{j}\equiv b_i(mod\ P)\),令\(i\bigoplus j=k\),则\(i=j\bigoplus k\)\(\sum_{j=1}^na_{i\bigoplus j}x_{j}=\sum_{j\bigoplus k=i}^na_kx_{j}=b_i\),所以就有\(FWT(a)\cdot FWT(x)=FWT(b)\),那么\(x\)就是\(FWT^{-1}(x)\)

一开始看到了底数异或就想到了FWT,但没怎么做过,所以没细想,于是走入了一个误区,先把\(x\)中元素的和求了出来,然后希望去解这个方程组,但数据范围太大,不行,显然需要nlogn的做法,于是想到了分治,但推不出来式子,菜菜菜

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 262145, P = 1e9+7;
int n, m;

void add(int &x, int y) {
    (x += y) >= P && (x -= P);
}
void sub(int &x, int y) {
    (x -= y) < 0 && (x += P);
}
ll power(ll x,int y)
{
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%P;
		x=x*x%P;
		y>>=1;
	}
	return res;
}
struct FWT {
    int extend(int n) {
        int N = 1;
        for (; N < n; N <<= 1);
        return N;
    }
    void FWTor( vector<int> &a, bool rev) {
        int n = a.size();
        for (int l = 2, m = 1; l <= n; l <<= 1, m <<= 1) {
            for (int j = 0; j < n; j += l) for (int i = 0; i < m; i++) {
                if (!rev) add(a[i + j + m], a[i + j]);
                else sub(a[i + j + m], a[i + j]);
            }
        }
    }
    void FWTand( vector<int> &a, bool rev) {
        int n = a.size();
        for (int l = 2, m = 1; l <= n; l <<= 1, m <<= 1) {
            for (int j = 0; j < n; j += l) for (int i = 0; i < m; i++) {
                if (!rev) add(a[i + j], a[i + j + m]);
                else sub(a[i + j], a[i + j + m]);
            }
        }
    }
    void FWTxor( vector<int> &a, bool rev) {
        int n = a.size(), inv2 = (P + 1) >> 1;
        //inv2=power(2,P-2);
        for (int l = 2, m = 1; l <= n; l <<= 1, m <<= 1) {
            for (int j = 0; j < n; j += l) for (int i = 0; i < m; i++) {
                int x = a[i + j], y = a[i + j + m];
                if (!rev) {
                    a[i + j] = (x + y) % P;
                    a[i + j + m] = (x - y + P) % P;
                } else {
                    a[i + j] = 1LL * (x + y) * inv2 % P;
                    a[i + j + m] = 1LL * (x - y + P) * inv2 % P;
                }
            }
        }
    }
    vector<int> Or(vector<int> a1, vector<int> a2) {
        int n = max(a1.size(), a2.size()), N = extend(n);
        a1.resize(N), FWTor(a1, false);
        a2.resize(N), FWTor(a2, false);
        vector<int> A(N);
        for (int i = 0; i < N; i++) A[i] = 1LL * a1[i] * a2[i] % P;
        FWTor(A, true);
        return A;
    }
    vector<int> And(vector<int> a1, vector<int> a2) {
        int n =  max(a1.size(), a2.size()), N = extend(n);
        a1.resize(N), FWTand(a1, false);
        a2.resize(N), FWTand(a2, false);
        vector<int> A(N);
        for (int i = 0; i < N; i++) A[i] = 1LL * a1[i] * a2[i] % P;
        FWTand(A, true);
        return A;
    }
     vector<int> Xor(vector<int> a1, vector<int> a2) {
        int n = max(a1.size(), a2.size()), N = extend(n);
        a1.resize(N), FWTxor(a1, false);
        a2.resize(N), FWTxor(a2, false);
        vector<int> A(N);
        for (int i = 0; i < N; i++) A[i] = 1LL * a1[i] * power(a2[i],P-2) % P;
        FWTxor(A, true);
        return A;
    }
} fwt;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
    int n;
    cin>>n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) cin>>a[i];
    for (int i = 0; i < n; i++) cin>>b[i];
    vector<int> A;
    A = fwt.Xor(b, a);
    for (int i = 0; i < n; i++) 
     cout<<A[i]<<" \n";
    return 0;
}

E.Music Game(区间、期望DP)

Problem

给定一个长度为\(n\)的敲击长度,每次敲击正确的概率为\(p_i\),定义一段连续敲击正确的长度\(x\)贡献为\(x^m\),问期望可以的得多少分。
\(1\le n,m\le 10^3\)

Sol

CF235B Let's Play Osu!

P1654 OSU!

OSU经典概率期望题,上面两个题数据范围较大,所以\(m\)小,可以线性递推。但这个题的\(n\)较小,\(m\)较大,做法应该是\(O(n^2)\)级别的。考虑\(dp[i][j]\)表示从\(i\)开始的长度为\(j\)的正确序列的概率,那么\(dp[i][j]=dp[i][j-1]*p[i]\),然后考虑长度为\(j\)的贡献,因为要保证此刻长度为\(j\)的贡献不会往两边延伸才可以计算,所以此时的期望贡献就是\(dp[i][j]*(1-p[i])*(1-p[i+j])*j^m\),表明第\(i-1\)和第\(i+j\)个位置是不正确的,此时长度为\(j\)才会产生贡献。

//区间、期望DP
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll power(ll x,int y)
{
  ll res=1;
  while(y)
  {
    if(y&1) res=res*x%mod;
    x=x*x%mod;
    y>>=1;
  }
  return res;
}
int main()
{
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  ll inv=power(100,mod-2);
  int n,m;
  cin>>n>>m;
  vector<ll>p(n+2),np(n+2);
  vector<vector<ll>>dp(n+1,vector<ll>(n+1));
  vector<ll>len(n+1);
    
  for(int i=0;i<=n;i++) len[i]=power(i,m); 
    len[0]=1;
  np[0]=p[0]=np[n+1]=p[n+1]=1;
  for(int i=1;i<=n;i++)
  {
     cin>>p[i];
     np[i]=(100-p[i])*inv%mod;
     p[i]=p[i]*inv%mod;
     dp[i][0]=1;
  }
  ll ans=0;
  //dp[i][j]:从i开始,长度为j的贡献
  for(int i=1;i<=n;i++)
    for(int j=1;i+j-1<=n;j++)
    {
       dp[i][j]=dp[i][j-1]*p[i+j-1]%mod;
       ans=(ans+dp[i][j]*np[i-1]%mod*np[i+j]%mod*len[j]%mod)%mod;
    }
    cout<<ans<<'\n';
}

好难啊

C.The number of circuits

G.Longest Common Subsequence

H.Prefix Sum

posted @ 2022-05-01 20:46  Arashimu  阅读(22)  评论(0编辑  收藏  举报