题目链接:acm.hdu.edu.cn/showproblem.php?pid=6589

题意:给出一个长度为n的数组,有m次操作,操作有3种1,2,3,问操作m次后的数组,输出i*a[i]的异或和

操作k的实质是进行一次O(n)的计算,a[i]+=a[i-k] (i-k>0)

k=1时,我们可以发现这是一次求前缀和的操作

k=2时,我们可以发现这是对于1,3,5,7... 2,4,6,8...两个子数组分别进行求前缀和的操作

k=3时,我们可以发现这是对于1,4,7,11...2,5,8,12...3,6,9,12...三个子数组分别求前缀和的操作

暴力的复杂度是O(mn),我们可以模拟出暴力的过程,其实这并不是一个浪费时间的过程,因为在比赛时,我们通过这个暴力的程序验算样例,发现了一个性质,那就是操作顺序的改变,并不会影响结果!

这个性质是解题的关键,如果没有发现这个性质,那么是想不到正解的,那么,问题的本质就变成了如何快速求出m次前缀和,粗略一想很显然这还是个o(nm)的操作,其实不然

观察求前缀和的过程

0次(不求):a[1],a[2],a[3],a[4],a[5]...

1次:         a[1],a[2]+a[1],a[3]+a[2]+a[1],a[4]+a[3]+a[2]+a[1],a[5]+a[4]+a[3]+a[2]+a[1]...

2次:         a[1],a[2]+2a[1],a[3]+2a[2]+3a[1],a[4]+2a[3]+3a[2]+4a[1],a[5]+2a[4]+3a[3]+4a[2]+5a[1]...

3次:         a[1],a[2]+3a[1],a[3]+3a[2]+6a[1],a[4]+3a[3]+6a[2]+10a[1],a[5]+3a[4]+6a[3]+10a[2]+15a[1]...

...

这里,规律就很明显了,我们可以发现进行多次前缀和后的数组,它的结果是和组合数有关的

第m次,组合数数组应该是c[i]=C(m+i-2,i-1),那么,上述结果用数组表示就是

m次:      c[1]*a[1],c[1]*a[2]+c[2]*a[1],c[1]*a[3]+c[2]*a[2]+c[3]*a[1],c[1]*a[4]+c[2]*a[3]+c[3]*a[2]+c[4]*a[1],c[1]*a[5]+c[2]*a[4]+c[3]*a[3]+c[4]*a[2]+c[5]*a[1]...

这个东西已经很明显了,就是数组a[1],a[2],a[3],a[4],a[5]... 与b[1],b[2],b[3],b[4],b[5]...求卷积的结果,组合数的求法,O(m)预处理,O(1)求解即可,这是个很经典的方法,这里就不再赘述,百度上很多

求卷积有NTT(快速数论变换)与FFT(快速傅立叶变换)两种方法,也许你并不会这两个方法,这没有关系,套模板就行了,对于k=2,k=3的情况,我们只需要将数组拆分成子数组,就可以变成k=1的形式了,问题也就解决了

值得一提的是,由于FFT是复数操作,存在浮点误差,而且取模是一个魔法操作(不会),所以这里还是用NTT比较合适,注意一个细节,由于要多次使用板子,所以每次用完一定要把板子里面应该重置的数据要初始化,

特别是那两个用来求卷积的数组!!!

做一次卷积,我们就可以得到n次前缀和后的数组,整体时间复杂度O(m+nlogn)

上代码:

#include <bits/stdc++.h>
using namespace std;
#define maxn 300005//注意用来求卷积的数组的大小
#define MOD 998244353
#define mod MOD
#define G 3
typedef long long ll;
namespace NTT {//模板内容
int rev[maxn], n, m;
long long A[maxn], B[maxn], C[maxn]; inline ll Pow(ll a, ll k) {
ll base = 1;
while (k) {
if (k & 1) base = (base * a) % MOD;
a = (a * a) % MOD;
k >>= 1;
}
return base % MOD;
} void NTT(long long *a, int len, int opt) {
for (int i = 0; i < len; i++) {
if (i < rev[i]) {
swap(a[i], a[rev[i]]);
}
}
for (int i = 1; i < len; i <<= 1) {
long long wn = Pow(G, (opt * ((MOD - 1) / (i << 1)) + MOD - 1) % (MOD - 1));
int step = i << 1;
for (int j = 0; j < len; j += step) {
long long w = 1;
for (int k = 0; k < i; k++, w = (1ll * w * wn) % MOD) {
long long x = a[j + k];
long long y = 1ll * w * a[j + k + i] % MOD;
a[j + k] = (x + y) % MOD;
a[j + k + i] = (x - y + MOD) % MOD;
}
}
}
if (opt == -1) {
long long r = Pow(len, MOD - 2);
for (int i = 0; i < len; i++)
a[i] = 1ll * a[i] * r % MOD;
}
} void solve(int n, int m) {
int x, l = 0 ,len = 1;
while (len <= n + m) len <<= 1, ++l;
for (int i = 0; i < len; ++i)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
NTT(A, len, 1), NTT(B, len, 1);
for (int i = 0; i < len; ++i){
C[i] = (ll) (A[i] * B[i]) % MOD;
A[i]=B[i]=0;
}
NTT(C, len, -1);
}
}
template <class T>
void read(T &x) {
static char ch;static bool neg;
for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
x=neg?-x:x;
}
int n,cnt[4];
ll a[100005],c[100005];
ll fac[1000005],inv[1000005];
ll pow_mod(ll m,ll n)
{
ll res=1;
while (n)
{
if(n&1)res=res*m%mod;
m=m*m%mod;
n>>=1;
}
return res;
}
void init()
{
inv[0]=fac[0]=1;
for(int i=1;i<=1000000;i++)fac[i]=fac[i-1]*i%mod;
inv[1000000]=pow_mod(fac[1000000],mod-2);
for(int i=999999;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(int n,int m)
{
if(m==0)return 1;//这个地方是为了特殊处理调cnt=0的情况,这个时候的c数组应该是1,0,0,0,0...
if(n-m<0)return 0;
return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
void calc(int m)//求m次前缀和的组合数数组
{
for(int i=1;i<=n;i++){
c[i]=C(m-2+i,i-1);
}
}
int main()
{
init();//组合数预处理
int T;
cin>>T;
while (T--)
{
memset(cnt,0, sizeof(cnt));
int m,op;
read(n);read(m);
for(int i=1;i<=n;i++)
{
read(a[i]);
}
for(int i=1;i<=m;i++)
{
read(op);
++cnt[op];
}
calc(cnt[1]);
for(int i=0;i<n;i++)NTT::A[i]=a[i+1];
for(int i=0;i<n;i++)NTT::B[i]=c[i+1];
NTT::solve(n,n);
for(int i=0;i<n;i++)a[i+1]=NTT::C[i];
calc(cnt[2]);
vector<int>d1,d2,d3;
for(int i=1;i<=n;i++)
{
i%2?d1.emplace_back(a[i]):d2.emplace_back(a[i]);
}
for(int i=0;i<d1.size();i++)NTT::A[i]=d1[i];
for(int i=0;i<d1.size();i++)NTT::B[i]=c[i+1];
NTT::solve(d1.size(),d1.size());
for(int i=1;i<=n;i+=2)a[i]=NTT::C[i/2];
for(int i=0;i<d2.size();i++)NTT::A[i]=d2[i];
for(int i=0;i<d2.size();i++)NTT::B[i]=c[i+1];
NTT::solve(d2.size(),d2.size());
for(int i=2;i<=n;i+=2)a[i]=NTT::C[i/2-1];
d1.clear();
d2.clear();
calc(cnt[3]);
for(int i=1;i<=n;i++)
{
if(i%3==1)d1.emplace_back(a[i]);
else if(i%3==2)d2.emplace_back(a[i]);
else d3.emplace_back(a[i]);
}
for(int i=0;i<d1.size();i++)NTT::A[i]=d1[i];
for(int i=0;i<d1.size();i++)NTT::B[i]=c[i+1];
NTT::solve(d1.size(),d1.size());
for(int i=1;i<=n;i+=3)a[i]=NTT::C[i/3];
for(int i=0;i<d2.size();i++)NTT::A[i]=d2[i];
for(int i=0;i<d2.size();i++)NTT::B[i]=c[i+1];
NTT::solve(d2.size(),d2.size());
for(int i=2;i<=n;i+=3)a[i]=NTT::C[i/3];
for(int i=0;i<d3.size();i++)NTT::A[i]=d3[i];
for(int i=0;i<d3.size();i++)NTT::B[i]=c[i+1];
NTT::solve(d3.size(),d3.size());
for(int i=3;i<=n;i+=3)a[i]=NTT::C[i/3-1];
ll ans=0;
for(int i=1;i<=n;i++)ans=ans^(1ll*i*a[i]);
cout<<ans<<endl;
}
return 0;
}

标程给了一个更好的思路,k=2时,我们把k=1的那种c数组变成c[1],0,c[2],0,c[3],0....

k=3时,变成c[1],0,0,c[2],0,0,c[3],0,0...这种,然后直接对两个数组求卷积就可以了

这是按照标程思路写的代码,精简了很多,常数也小了一些

#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
#define MOD 998244353
#define mod MOD
#define G 3
typedef long long ll;
int rev[maxn];
long long C[maxn]; inline ll Pow(ll a, ll k) {
ll base = ;
while (k) {
if (k & ) base = (base * a) % MOD;
a = (a * a) % MOD;
k >>= ;
}
return base % MOD;
} void NTT(long long *a, int len, int opt) {
for (int i = ; i < len; ++i) {
if (i < rev[i]) {
swap(a[i], a[rev[i]]);
}
}
for (int i = ; i < len; i <<= ) {
long long wn = Pow(G, (opt * ((MOD - ) / (i << )) + MOD - ) % (MOD - ));
int step = i << ;
for (int j = ; j < len; j += step) {
long long w = ;
for (int k = ; k < i; ++k, w = (1ll * w * wn) % MOD) {
long long x = a[j + k];
long long y = 1ll * w * a[j + k + i] % MOD;
a[j + k] = (x + y) % MOD;
a[j + k + i] = (x - y + MOD) % MOD;
}
}
}
if (opt == -) {
long long r = Pow(len, MOD - );
for (int i = ; i < len; i++)
a[i] = 1ll * a[i] * r % MOD;
}
} void solve(ll A[],ll B[],int n, int m) {
int x, l = ,len = ;
while (len <= n + m) len <<= , ++l;
for (int i = ; i < len; ++i)
rev[i] = (rev[i >> ] >> ) | ((i & ) << (l - ));
NTT(A, len, ), NTT(B, len, );
for (int i = ; i < len; ++i) {
C[i] = (ll) (A[i] * B[i]) % MOD;
A[i] = B[i] = ;
}
NTT(C, len, -);
}
void read(ll &x) {
static char ch;static bool neg;
for(ch=neg=;ch<'' || ''<ch;neg|=ch=='-',ch=getchar());
for(x=;''<=ch && ch<='';(x*=)+=ch-'',ch=getchar());
x=neg?-x:x;
}
int n,cnt[];
ll a[maxn],c[maxn];
ll fac[],inv[];
ll pow_mod(ll m,ll n)
{
ll res=;
while (n)
{
if(n&)res=res*m%mod;
m=m*m%mod;
n>>=;
}
return res;
}
void init()
{
inv[]=fac[]=;
for(int i=;i<=;i++)fac[i]=fac[i-]*i%mod;
inv[]=pow_mod(fac[],mod-);
for(int i=;i>=;i--)inv[i]=inv[i+]*(i+)%mod;
}
ll Comb(int n,int m)
{
return n<m?:fac[n]*inv[n-m]%mod*inv[m]%mod;
}
int main()
{
init();//组合数预处理
int T;
cin>>T;
while (T--)
{
memset(cnt,, sizeof(cnt));
ll m,op;
cin>>n>>m;
for(int i=;i<=n;i++)
{
read(a[i]);
}
for(int i=;i<=m;i++)
{
read(op);
++cnt[op];
}
for(int i=;i<=;i++)
{
memset(c,, sizeof(c));
for(int j=;j*i<n;j++)
{
c[j*i]=Comb(cnt[i]-+j,j);
}
if(cnt[i]==)c[]=;//特殊处理
solve(a+,c,n,n);
for(int i=;i<n;i++)a[i+]=C[i];
}
ll ans=;
for(int i=;i<=n;i++)ans=ans^(1ll*i*a[i]);
cout<<ans<<endl;
}
return ;
}

总结:这个题总体来说还是不难的,虽然过程繁琐,比赛的时候用了FFT也没写出来,不过总的收获还是很大的,以前对于这种比赛时过的很少的题束手无策,现在也能自己分析个七七八八的,算是一种进步了吧

多想想,不要轻易放弃,也许下一刻就能收获AC!

HDU多校训练第一场 1012 Sequence的更多相关文章

  1. 牛客网多校训练第一场 I - Substring(后缀数组 + 重复处理)

    链接: https://www.nowcoder.com/acm/contest/139/I 题意: 给出一个n(1≤n≤5e4)个字符的字符串s(si ∈ {a,b,c}),求最多可以从n*(n+1 ...

  2. HDU多校练习第一场4608——I_Number

    题目:点击打开链接 水题一道,刚开始写了一发模拟,后来发现所谓的10^5是个length……果断加了个大数枚举,过了,今天换了个样式重写了个. 易于推出,两个数之间的最大差值为20. #include ...

  3. 牛客网多校训练第一场 J - Different Integers(树状数组 + 问题转换)

    链接: https://www.nowcoder.com/acm/contest/139/J 题意: 给出n个整数的序列a(1≤ai≤n)和q个询问(1≤n,q≤1e5),每个询问包含两个整数L和R( ...

  4. 牛客网多校训练第一场 F - Sum of Maximum(容斥原理 + 拉格朗日插值法)

    链接: https://www.nowcoder.com/acm/contest/139/F 题意: 分析: 转载自:http://tokitsukaze.live/2018/07/19/2018ni ...

  5. 牛客网多校训练第一场 E - Removal(线性DP + 重复处理)

    链接: https://www.nowcoder.com/acm/contest/139/E 题意: 给出一个n(1≤n≤1e5)个整数(范围是1至10)的序列,求从中移除m(1≤m≤min(n-1, ...

  6. 牛客网多校训练第一场 D - Two Graphs

    链接: https://www.nowcoder.com/acm/contest/139/D 题意: 两个无向简单图都有n(1≤n≤8)个顶点,图G1有m1条边,图G2有m2条边,问G2有多少个子图与 ...

  7. 牛客网多校训练第一场 B - Symmetric Matrix(dp)

    链接: https://www.nowcoder.com/acm/contest/139/B 题意: 求满足以下条件的n*n矩阵A的数量模m:A(i,j) ∈ {0,1,2}, 1≤i,j≤n.A(i ...

  8. 牛客网多校训练第一场 A - Monotonic Matrix(Lindström–Gessel–Viennot lemma)

    链接: https://www.nowcoder.com/acm/contest/139/A 题意: 求满足以下条件的n*m矩阵A的数量模(1e9+7):A(i,j) ∈ {0,1,2}, 1≤i≤n ...

  9. 19暑假多校训练第一场-J-Fraction Comparision(大数运算)

    链接:https://ac.nowcoder.com/acm/contest/881/J来源:牛客网 题目描述 Bobo has two fractions xaxa and ybyb. He wan ...

随机推荐

  1. windows安装 阿里云的Fun工具

    由于项目使用到了 函数计算,特此了解到了需要安装 阿里云的Fun工具 Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算.API 网关.日志服务等资源.它通过一个 ...

  2. day03 for循环、字符串方法、类型转换

    01 上周内容回顾 while 条件: 循环体 例: while True: print(111) print(222) print(333) 结束循环的两种方式: 1,改变条件. 2,break. ...

  3. React(5) --绑定函数事件

    绑定函数事件 在以类继承的方式定义的组件中,为了能方便地调用当前组件的其他成员方法或属性(如:this.state),通常需要将事件处理函数运行时的 this 指向当前组件实例. run(){     ...

  4. jffs2镜像制作

    自己被自己绊住了,出于对无知的恐惧,总觉得是很难的一件事情. demo board ltp-ddt qspi_mtd_dd_rw error:can't read superblock on /dev ...

  5. mongodb 索引分类

    一. 普通索引篇 1.创建索引 创建索引:db.person.ensureIndex({"age":1}).这里我们使用了ensureIndex在age上建立了索引.“1”:表示按 ...

  6. Linux的运行级别和设置开机启动服务的方式

    Linux的运行级别 什么是运行级别呢?简单点来说,运行级别就是操作系统当前正在运行的功能级别.级别是从0到6,具有不同的功能.这些级别定义在/ect/inittab文件中.这个文件是init程序寻找 ...

  7. phpstorm ftp不能连接服务器

    环境: ubuntu phpstorm 问题一. 服务器ftp功能没有开启 解决方法:在服务器上安装 ftp 服务 https://i.cnblogs.com/EditPosts.aspx?posti ...

  8. 用pycharm运行pytest

    安装pytest 1. 在pycharm中建项目,建文件,文件名字要以test_开头 2.在文件中插入pytest模块 import pytest #引用pytest模块 3.定义test函数,以及断 ...

  9. PowerDesigner 小技巧

    PowerDesigner 重现快捷工具栏 palette :Tools -->customsize toolbars-->palette(调色板)勾选 如何在PDM中列表显示TABLE的 ...

  10. VS2015 Bad Request解决方法

    新获取的项目,使用vs2015启动项目,遇到只能用localhost:xxxx的方式访问,使用192.168.**.**:xxxx这样ip+端口的方式无法访问的情况 原因:vs没有做出相应的配置 解决 ...