目前处于康复期,所以做多一点题目……感觉自己码力退步了QAQ连初二的时候都比不上了(现在连LCT都写不动了……颓颓颓)
LOJ 2038 「SHOI2015」超能粒子炮・改
题意:求
∑i=0k(ni)(mod2333)
,其中
n,k≤1018
分析:一般组合数取模的题目我们都可以马上想到Lucas定理,而这题我们稍微转化下就可以了
题解:先化一波式子,由于
2333
是质数,那么原式即
然后 i%2333 有一个模运算的周期性,同时这里面 i/2333 又是相等的,我们考虑分块处理,只需要考虑再考虑最后不单独成一块的那一部分,暴力即可
成整块的前一部分是 ∑i=0k2333−1(n/2333i)×∑i=02332(n%2333i)
然后剩下来的就是 ∑i=0k%2333(n/2333i/2333)(n%2333k%2333)
我们将 S(n,k) 代进去,得到
# include <cstdio>
using namespace std;
const int mod = 2333;
int c[mod+20][mod+20], f[mod+20][mod+20];
inline int lucas(long long i, long long j){
if (i < mod && j < mod) return c[i][j];
return c[i%mod][j%mod]*lucas(i/mod, j/mod)%mod;
}
inline int calcCsum(long long i, long long j){
if (i < mod && j < mod) return f[i][j];
return (f[i%mod][mod-1]*calcCsum(i/mod, (j+1)/mod-1)+lucas(i/mod, j/mod)*f[i%mod][(j+1)%mod-1])%mod;
}
int main(){
c[0][0] = 1;
for (int i = 1; i < mod; i++)
for (int j = 0; j <= i; j++)
c[i][j] = (c[i-1][j]+(j>=1)*c[i-1][j-1])%mod;
for (int i = 0; i < mod; i++) {
f[i][0] = 1;
for (int j = 1; j < mod; j++)
f[i][j] = (f[i][j-1]+c[i][j])%mod;
}
int T; scanf("%d", &T);
while (T--) {
long long n, m;
scanf("%lld%lld", &n, &m);
printf("%d\n", calcCsum(n, m));
}
return 0;
}
LOJ2028 「SHOI2016」随机序列
题意:一个序列
ai
,两个相邻数之间可以插入加号、减号或乘号,每次不断修改其中某个数并询问所有可能的表达式的和
分析:先考虑没修改怎么做,考虑一个DP,设
f(i)
表示前
i
个数能组成的表达式的和,
但是这是第一眼想的思路,稍微再想想我们会发现这好像想多了……每一个加号都会有一个减号消掉他,所以只有第一个是有贡献的,那么就是剩下的方案数和前面的乘积和,即
∑i=1n2×3n−i∏j=1iaj
一开始可以暴力统计一遍,然后每次修改可以直接用逆元把后面的给做掉,相当于后面的区间乘一个数,用线段树维护前缀乘积就好了
code没调,先存着
# include <cstido>
# define M 1000000007
# define ms(x) (x>=mod?x-mod:x)
using namespace std;
namespace SegT{
int sum[S],tag[S];
inline void pup(int x){ sum[x]=ms(sum[x<<1]+sum[x<<1|1]); }
inline void ptg(int x,int d){ sum[x]=(sum[x]*d)%mod; }
inline void pdn(int x){ if (tag[x]!=1) ptg(x<<1,tag[x]),ptg(x<<1|1,tag[x]); tag[x]=1; }
void init(int x,int l,int r){
if (l==r){ sum[x]=p[l]*a[l]; tag[x]=1; return; }
if (l<r){
tag[x]=1;
int m = (l+r)>>1;
init(x<<1,l,m),init(x<<1|1,m+1,r);
pup(x);
}
}
int opt,opans,opl,opr,opg;
void tend(int x,int l,int r){
if (opl <= l && r <= opr){ //in intervals
if (opt == 1){ opans = ms(opans+sum[x]*tag[x]); }
if (opt == 2){ tag[x]=(tag[x]*opg)%mod; }
}
else{
int m=(l+r)>>1; pdn(x);
if (opl <= m) tend(x<<1,l,m);
if (opr > m) tend(x<<1|1,m+1,r);
pup(x);
}
}
}
int a[N],n,q;
int main(){
scanf("%d%d",&n,&q);
for (int i=1;i<=n;++i) scanf("%d",a+i);
for (int i=2;i<=n;++i) a[i] = (a[i] * a[i-1]) % mod;
p[n]=2; for (int i=1;i<n;++i) p[i]=ms(p[i+1]+ms(p[i+1]+p[i+1]));
SegT::init(1,1,n);
for (int i=0;i<q;++i){
int t,v; scanf("%d%d",&t,&v);
SegT::opt=2; SegT::opl=SegT::opr=t; SegT::opg=v;
SegT::tend(1,1,n);
SegT::opt=1; SegT::opl=1,SegT::opr=n; SegT::opans=0;
SegT::tend(1,1,n);
printf("%d\n",SegT::opans);
}
return 0;
}
「CodePlus 2017 12 月赛」可做题2
题意:问对于第一项是给定的
i
,第二项
分析:第一眼,发现这里中间Fibonacci的转移矩阵是相同的!于是可以直接快速幂求出
另外还有Fibonacci数列取模循环节的方法,这里就暂且不提了
# include <cstring>
# include <iostream>
# include <cstdio>
# include <cmath>
# define mem(x,v) memset(x,v,sizeof(x))
# define ms(x) (x>=p?x-p:x)
using namespace std;
typedef long long LL;
LL i,l,r,k,p,m;
struct matrix{
LL a[2][2];
matrix(){ mem(a,0); }
matrix operator*(const matrix t){
matrix ans;
for (int i=0;i<2;++i)
for (int j=0;j<2;++j)
for (int k=0;k<2;++k)
ans.a[i][j]=(ans.a[i][j]+(a[i][k]*t.a[k][j])%p)%p;
return ans;
}
void print(){
cerr << a[0][0] <<' ' << a[0][1] << endl;
cerr << a[1][0] <<' ' << a[1][1] << endl;
}
};
matrix fast_pow(matrix x,LL k){
matrix t; t.a[0][0]=t.a[1][1]=1;
for ( ;k;k>>=1,x=x*x) if (k&1) t=t*x;
return t;
}
void extgcd(LL a,LL b,LL&d,LL &x,LL &y){
if(!b){d=a;x=1,y=0;}
else{
extgcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}
int main(){
// freopen("inp.in","r",stdin);
// freopen("myp.out","w",stdout);
matrix fib; fib.a[0][1]=fib.a[1][0]=fib.a[1][1]=1;
int T; cin >> T;
while (T--){
cin >> i >> l >> r >> k >> p >> m;
i%=p;
matrix t=fast_pow(fib,k-1); //t.print(); cout << endl;
// swap(t.a[0][1],t.a[0][0]) ;
LL d,k,a2; extgcd(p,t.a[0][1],d,k,a2);
LL c=(m - t.a[0][0]*i);
if (c < 0){ c = p - ((-c) % p); }
if(c % d != 0) cout << 0 << endl;
else{
//a2+pk=21 ?
a2 *= c/d; k*= c/d;
a2 += ((l-a2) / (p/d)) * (p / d);
while (a2 > l){ a2 -= p/d; }
while (a2 < l){ if (a2 > r){cout << 0 << endl; continue;} a2 += p/d; }
// cerr << "Now l=" << l <<" now a2=" << a2 << " now spa = " << p << '/' << d << endl;
// cerr << (r-a2) << "\n" << (p/d) << endl;
LL num = (((r-a2) / (p/d))+1); cout << (r>=a2?num:0) << endl;
}
}
return 0;
}