北京集训:20180314
T1:
大力矩阵乘法有40分,我也就会这么做了。
以下为官方题解:
(至今仍然不会常系数线性递推的蒟蒻......)
40分代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 typedef long long int lli; 6 using namespace std; 7 const int maxm=210; 8 const int mod=998244353,base=19260817; 9 10 int now; 11 struct Matrix { 12 lli dat[maxm][maxm]; 13 Matrix(int tpe=0) { 14 memset(dat,0,sizeof(dat)); 15 if( tpe ) for(int i=1;i<=now;i++) dat[i][i] = 1; 16 } 17 lli* operator [] (const int &x) { 18 return dat[x]; 19 } 20 friend Matrix operator * (const Matrix &a,const Matrix &b) { 21 Matrix ret(0); 22 for(int i=1;i<=now;i++) 23 for(int j=1;j<=now;j++) 24 for(int k=1;k<=now;k++) 25 ( ret[i][j] += a.dat[i][k] * b.dat[k][j] % mod ) %= mod; 26 return ret; 27 } 28 }ini,trans,ans; 29 30 int m,a,n; 31 inline void build() { 32 ini = trans = Matrix(0); 33 for(int i=1;i<=now;i++) trans[i][now] = ( a - 1 + mod ) % mod; 34 for(int i=1;i<now;i++) trans[i+1][i] = 1; 35 ini[1][1] = a % mod; 36 for(int i=2;i<=now;i++) ini[1][i] = ini[1][i-1] * a % mod; 37 } 38 inline Matrix fastpow(Matrix base,int tim) { 39 Matrix ret(1); 40 while(tim) { 41 if( tim & 1 ) ret = ret * base; 42 if( tim >>= 1 ) base = base * base; 43 } 44 return ret; 45 } 46 inline lli calc(int nn) { 47 now = nn , build(); 48 if( n <= now ) return ini[1][n]; 49 ans = ini * fastpow(trans,n-now); 50 return ans[1][now]; 51 } 52 inline lli getans() { 53 lli ret = 0 , mul = 1; 54 for(int i=0;i<=m;i++) { 55 ret += calc(i) * mul % mod , 56 ret %= mod , 57 mul = mul * base % mod; 58 } 59 return ret; 60 } 61 62 int main() { 63 scanf("%d%d%d",&m,&a,&n); 64 printf("%lld\n",getans()); 65 return 0; 66 }
Commit@2018.03.15
蒟蒻终于会常系数线性递推啦!
首先我们有一个转移矩阵
[0,0,a1]
[1,0,a2]
[0,1,a3]
如果我们大力矩阵快速幂的话,复杂度是O(k^3logn)的,现在我们要想一个办法把他优化到O(k^2logn)
我们知道矩阵A的特征多项式f(x)=|A-E*x|,其中E为单位矩阵。
就是说,f(x)为矩阵A减去x倍单位矩阵后矩阵的行列式。
然后我们有一个神奇的定理:如果我们把矩阵A当做自变量x带入f(x)中的话,我们会发现f(A)=0(证明什么的,感性理解吧)。
这样的话,g(A)就等于g(A) mod f(A)。(考虑取模的意义,这很显然)
而我们现在要求A的t次方,故g(A)=A^t,然后我们大力多项式取模一发就能用一个次数界为k的多项式表示g(A)。
这个取模怎么做?我们可以快速幂倍增取模,利用FFT+多项式求逆可以做到klogk的复杂度,而这里我们直接k^2大力计算就好了。
然后我们得到了一个多项式,怎么做?
我们需要的答案为另一个矩阵B*g(A),也就是B*(g(A) mod f(A))。
考虑这个多项式的意义,每一项B*A^p代表转移p次后获得的值。
好,现在我们考虑怎么丢掉矩阵这个东西(因为一次矩乘就O(k^3)了)。
如果我们大力预处理出原数列转移p次的值,然后用这个多项式的系数和预处理出的值相乘,不就直接得到我们需要的答案了吗?
也就是说,我们的答案为:sigma(C[i]*PRE[i+k]),C为我们取模后的多项式。
然而我们还不知道特征多项式怎么算。
如果矩阵没什么性质的话,我们可以大力插值+高斯消元计算k+1个点值,然后用DFT或者拉格朗日插值算回去就好。
然而,常系数线性递推的特征多项式是有性质的,他就是x^k-sigma(a[i]*x^(k-i))。
此外你把系数放在右边和左边都是一样的,因为这两个矩阵互为转置,而转置矩阵的行列式相同。
最后上代码(60分):
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define debug cout 6 typedef long long int lli; 7 using namespace std; 8 const int maxn=4e2+1e1; 9 const int mod=998244353,base=19260817; 10 11 int k; 12 inline lli fastpow(lli base,int tim) { 13 lli ret = 1; 14 while( tim ) { 15 if( tim & 1 ) ret = ret * base % mod; 16 if( tim >>= 1 ) base = base * base % mod; 17 } 18 return ret; 19 } 20 inline lli inv(const lli &x) { 21 return fastpow(x,mod-2); 22 } 23 struct Poly { 24 lli dat[maxn]; 25 Poly() { 26 memset(dat,0,sizeof(dat)); 27 } 28 lli& operator [] (const int &x) { 29 return dat[x]; 30 } 31 const lli& operator [] (const int &x) const { 32 return dat[x]; 33 } 34 friend Poly operator * (const Poly &a,const Poly &b) { 35 Poly ret; 36 for(int i=0;i<k;i++) 37 for(int j=0;j<k;j++) 38 ( ret[i+j] += a[i] * b[j] % mod ) %= mod; 39 return ret; 40 } 41 friend Poly operator % (const Poly &a,const Poly &b) { 42 Poly ret = a; 43 int lst = k; 44 while( !b[lst] ) --lst; 45 for(int i=(k<<1);i>=lst;i--) 46 if( ret[i] ) { 47 const lli mul = ret[i] * inv(b[lst]); 48 for(int t=0;t<=lst;t++) 49 ret[i-t] = ( ret[i-t] - b[lst-t] * mul % mod + mod ) % mod; 50 } 51 return ret; 52 } 53 }ini,trans,ans; 54 55 lli ins[maxn]; 56 int m,a,n; 57 58 inline Poly fastpow(Poly base,const Poly &mod,int tim) { 59 Poly ret = base; --tim; 60 while( tim ) { 61 if( tim & 1 ) ret = ret * base % mod; 62 if( tim >>= 1 ) base = base * base % mod; 63 } 64 return ret; 65 } 66 67 inline lli calc(int kk) { 68 k = kk + 1; 69 trans[kk] = 1; 70 for(int i=0;i<kk;i++) trans[i] = ( 1 - a + mod ) % mod; 71 if( n <= kk ) return fastpow(a,n); 72 ins[0] = 1; 73 for(int i=1;i<=kk;i++) ins[i] = ins[i-1] * a % mod; 74 for(int i=kk+1;i<=kk<<1;i++) { 75 ins[i] = 0; 76 for(int t=1;t<=kk;t++) { 77 ( ins[i] += ins[i-t] * ( a - 1 ) % mod ) %= mod; 78 } 79 } 80 ini[1] = 1; 81 ans = fastpow(ini,trans,n-kk); 82 lli ret = 0; 83 for(int i=0;i<k;i++) ( ret += ins[i+k-1] * ans[i] % mod ) %= mod; 84 return ret; 85 } 86 87 int main() { 88 static lli ans,mul=1; 89 scanf("%d%d%d",&m,&a,&n); 90 for(int i=0;i<=m;i++) ( ans += mul * calc(i) % mod ) %= mod , ( mul *= base ) %= mod; 91 printf("%lld\n",ans); 92 return 0; 93 }
T2:
这题部分分好多啊......
前20分直接暴力。
接下来20分先构建广义SAM然后倍增主席树。
然后20分可以用LCT维护size。
最后20分直接动态开点权值线段树好了。
正解是后缀平衡树,并不会......
另外广义SAM加大力pb_ds也可过80分......
40分代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #define debug cout 7 typedef long long int lli; 8 using namespace std; 9 10 int n,m; 11 12 namespace Part1 { // 20 pts for brute force . 13 const int maxn=2e2+1e1; 14 int fa[maxn],in[maxn],len; 15 char s[maxn],val[maxn]; 16 inline void add(int f,int i,char v) { 17 fa[++n] = f , in[n] = i , val[n] = v; 18 } 19 inline void str(int pos) { 20 len = 0; 21 while(pos) s[++len] = val[pos] , pos = fa[pos]; 22 } 23 inline bool judge(int p,int lim) { 24 int ret = 0 , now = 1; 25 while( p && now < len ) { // last char must be '\0' 26 if( ret >= lim ) return 1; 27 if( val[p] != s[now] ) return 0; 28 ++ret , ++now , p = fa[p]; 29 } 30 return ret >= lim; 31 } 32 inline int query(int p,int lim,int limin) { 33 int ret = 0; 34 str(p); 35 for(int i=1;i<=n;i++) 36 if( in[i] <= limin && judge(i,lim) ) 37 ++ret; 38 return ret; 39 } 40 inline void solve() { 41 static int lastans = 1 , ans = 0; 42 static char ss[10]; 43 for(int i=1;i<=n;i++) scanf("%d",in+i); 44 for(int i=1,u,v;i<n;i++) { 45 scanf("%d%d%s",&u,&v,ss); 46 fa[v] = u , val[v] = *ss; 47 } 48 for(int i=1,o,u,l,r;i<=m;i++) { 49 scanf("%d",&o); 50 if( o == 0 ) { 51 scanf("%d%d%d",&u,&l,&r); 52 u ^= lastans; 53 ans = query(u,l,r) , printf("%d\n",ans); 54 if( ans ) lastans = ans; 55 } else if( o == 1 ) { 56 scanf("%d%d%s",&u,&l,ss); 57 u ^= lastans; 58 add(u,l,*ss); 59 } 60 } 61 } 62 } 63 namespace Part4 { 64 const int maxn=1e5+1e2; 65 int lson[maxn<<4],rson[maxn<<4],sum[maxn<<4],cnt=1; 66 inline void insert(int pos,int l,int r,int tar) { 67 ++sum[pos]; 68 if( l == r ) return; 69 const int mid = ( l + r ) >> 1; 70 if( tar <= mid ) { 71 if( !lson[pos] ) lson[pos] = ++cnt; 72 insert(lson[pos],l,mid,tar); 73 } else { 74 if( !rson[pos] ) rson[pos] = ++cnt; 75 insert(rson[pos],mid+1,r,tar); 76 } 77 } 78 inline int query(int pos,int l,int r,int ll,int rr) { 79 if( !pos ) return 0; 80 if( ll <= l && r <= rr ) return sum[pos]; 81 const int mid = ( l + r ) >> 1; 82 if( rr <= mid ) return query(lson[pos],l,mid,ll,rr); 83 else if( ll > mid ) return query(rson[pos],mid+1,r,ll,rr); 84 return query(lson[pos],l,mid,ll,rr) + query(rson[pos],mid+1,r,ll,rr); 85 } 86 inline void solve() { 87 static int ans = 0; 88 for(int i=1,x;i<=n;i++) { 89 scanf("%d",&x); 90 insert(1,1,1e9,x); 91 } 92 for(int i=1;i<n;i++) scanf("%*d%*d%*s"); 93 for(int i=1,o,l,r;i<=m;i++) { 94 scanf("%d",&o); 95 if( o == 0 ) { 96 scanf("%*d%*d%d",&r); 97 ans = query(1,1,1e9,1,r) , printf("%d\n",ans); 98 } else if( o == 1 ) { 99 scanf("%*d%d%*s",&l); 100 insert(1,1,1e9,l); 101 } 102 } 103 } 104 } 105 106 int main() { 107 scanf("%d%d",&n,&m); 108 if( n <= 100 && m <= 100 ) Part1::solve(); 109 else Part4::solve(); 110 }
80分代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<ext/pb_ds/assoc_container.hpp> 4 #define debug cerr 5 using namespace std; 6 using namespace __gnu_pbds; 7 typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> Tree; 8 const int maxn=8e4+1e2; 9 10 struct InNode { 11 int u,v,c,a; 12 friend bool operator < (const InNode &a,const InNode &b) { 13 return a.v < b.v; 14 } 15 }ins[maxn>>1]; 16 int ch[maxn<<1][4],len[maxn<<1],fa[maxn<<1],root,last; 17 Tree sons[maxn<<1]; 18 int at[maxn]; 19 int n,m; 20 21 inline int NewNode(int ll) { 22 static int cnt = 0; 23 len[++cnt] = ll; 24 return cnt; 25 } 26 inline void pushson(int x,int v) { 27 while(x) sons[x].insert(v) , x = fa[x]; 28 } 29 inline void extend(int p,int x,int nv) { 30 int np = NewNode(len[p]+1); 31 while( p && !ch[p][x] ) ch[p][x] = np , p = fa[p]; 32 if( !p ) fa[np] = root; 33 else { 34 int q = ch[p][x]; 35 if( len[q] == len[p] + 1 ) fa[np] = q; 36 else { 37 int nq = NewNode(len[p]+1); 38 memcpy(ch[nq],ch[q],sizeof(ch[q])) , fa[nq] = fa[q]; 39 fa[np] = fa[q] = nq; 40 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p]; 41 sons[nq] = sons[q]; 42 } 43 } 44 pushson(np,nv); 45 last = np; 46 } 47 inline int query(int pos,int lim,int mx) { 48 if( lim > len[pos] ) return 0; 49 while( fa[pos] && len[fa[pos]] >= lim ) pos = fa[pos]; 50 return sons[pos].order_of_key(mx+1); 51 } 52 inline void insert(int f,int x,int nv) { 53 extend(at[f],x,nv) , at[++n] = last; 54 } 55 56 inline int cov(char x) { 57 switch(x) { 58 case 'A' : return 0; 59 case 'T' : return 1; 60 case 'C' : return 2; 61 case 'G' : return 3; 62 } 63 } 64 65 int main() { 66 static int lim,lastans=1,ans,tp; 67 static char ss[10]; 68 scanf("%d%d",&lim,&m); 69 at[n=1] = last = root = NewNode(0); 70 scanf("%d",&tp) , sons[1].insert(tp); 71 for(int i=1;i<lim;i++) scanf("%d",&ins[i].a); 72 for(int i=1;i<lim;i++) { 73 scanf("%d%d%s",&ins[i].u,&ins[i].v,ss); 74 ins[i].c = cov(*ss); 75 } 76 sort(ins+1,ins+lim); 77 for(int i=1;i<lim;i++) insert(ins[i].u,ins[i].c,ins[i].a); 78 for(int i=1,o,u,l,r,f,a;i<=m;i++) { 79 scanf("%d",&o); 80 if( o == 0 ) { 81 scanf("%d%d%d",&u,&l,&r) ; u ^= lastans; 82 ans = query(at[u],l,r) , printf("%d\n",ans); 83 if( ans ) lastans = ans; 84 } else if( o == 1 ) { 85 scanf("%d%d%s",&f,&a,ss) ; f ^= lastans; 86 insert(f,cov(*ss),a); 87 } 88 } 89 return 0; 90 }
T3:
这题我看错题爆零了。
题意是每组里有x个则贡献为x/2。
我认为是这组必须全了才贡献x/2,根本不可做。
正解显然是一般图最大匹配。
还有某大佬的乱搞算法,就是暴力贪心寻找奇偶性的增广路,也能AC。
爆零代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #define debug cout 7 typedef long long int lli; 8 using namespace std; 9 10 int n,m; 11 12 namespace Part1 { 13 const int maxn=6,maxs=(1<<6)+2,maxm=12; 14 lli ned[maxm],val[maxm],f[maxm][maxs],ans; 15 int full; 16 17 inline void dp() { 18 memset(f,0,sizeof(f)) , ans = **f; 19 for(int i=1;i<=m;i++) { 20 for(int j=0;j<full;j++) if( ! ( j & ned[i] ) ) f[i][j|ned[i]] = max( f[i][j|ned[i]] , f[i-1][j] + val[i] ); 21 for(int j=0;j<full;j++) f[i][j] = max( f[i][j] , f[i-1][j] ); 22 } 23 for(int j=0;j<full;j++) ans = max( ans , f[m][j] ); 24 } 25 inline void solve() { 26 full = 1 << n; 27 for(int i=1,x,t;i<=m;i++) { 28 scanf("%d",&t) , val[i] = t >> 1 , ned[i] = 0; 29 while( t-- ) scanf("%d",&x) , ned[i] |= ( 1 << ( x - 1 ) ); 30 } 31 dp(); 32 printf("%lld\n",ans); 33 } 34 } 35 36 namespace Part2 { 37 const int maxn=5e2+1e1,maxm=1.5e3+1e2; 38 int s[maxn],t[maxm<<3],nxt[maxm<<3],f[maxm<<3],cnt; 39 lli c[maxm<<3],dis[maxn]; 40 int inq[maxn],sou[maxn]; 41 int st,ed; 42 inline void coredge(int from,int to,int flow,int cost) { 43 t[++cnt] = to , f[cnt] = flow , c[cnt] = cost , 44 nxt[cnt] = s[from] , s[from] = cnt; 45 } 46 inline void singledge(int from,int to,int flow,int cost) { 47 coredge(from,to,flow,cost) , coredge(to,from,0,-cost); 48 } 49 inline bool spfa() { 50 memset(dis,0xef,sizeof(dis)) , dis[st] = 0; 51 queue<int> q; q.push(st) , inq[st] = 1; 52 while( q.size() ) { 53 const int pos = q.front(); q.pop() , inq[pos] = 0; 54 for(int at=s[pos];at;at=nxt[at]) 55 if( f[at] && dis[t[at]] < dis[pos] + c[at] ) { 56 dis[t[at]] = dis[pos] + c[at] , sou[t[at]] = at; 57 if( !inq[t[at]] ) q.push(t[at]) , inq[t[at]] = 1; 58 } 59 } 60 return dis[ed] > 0; 61 } 62 inline int release() { 63 int ret = 0x3f3f3f3f; 64 for(int i=ed;i!=st;i=t[sou[i]^1]) ret = min( ret , f[sou[i]] ); 65 for(int i=ed;i!=st;i=t[sou[i]^1]) f[sou[i]] -= ret , f[sou[i]^1] += ret; 66 return ret; 67 } 68 inline lli flow() { 69 lli ret = 0; 70 while( spfa() ) { 71 ret += dis[ed] * release(); 72 } 73 return ret; 74 } 75 inline void solve() { 76 memset(s,0,sizeof(s)) , cnt = 0 , st = n + 1 , ed = n + 2; 77 for(int i=1,t,x,y;i<=m;i++) { 78 scanf("%d%d%d",&t,&x,&y); 79 if( y & 1 ) swap(x,y); 80 singledge(x,y,1,t>>1); 81 } 82 for(int i=1;i<=n;i++) 83 if( i & 1 ) singledge(st,i,1,0); 84 else singledge(i,ed,1,0); 85 printf("%lld\n",flow()); 86 } 87 } 88 89 int main() { 90 while( scanf("%d%d",&n,&m) == 2 && ( n || m ) ) { 91 if( n <= 6 && m <= 10 ) Part1::solve(); 92 else if( n <= 500 && m <= 1500 ) Part2::solve(); 93 } 94 return 0; 95 }
乱搞代码:
1 #include<cstdio> 2 #include<cstring> 3 const int maxn=3e3+1e2; 4 5 struct Graph { 6 int s[maxn],t[maxn<<1],nxt[maxn<<1],cnt; 7 inline void addedge(int from,int to) { 8 t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt; 9 } 10 inline int start(int x) { 11 return s[x]; 12 } 13 inline void ite(int &x) { 14 x = nxt[x]; 15 } 16 inline void reset() { 17 memset(s,0,sizeof(s)) , cnt = 0; 18 } 19 }g,rg; 20 21 int bel[maxn],siz[maxn],visl[maxn],visr[maxn]; 22 int n,m; 23 int tim; 24 25 inline bool dfs(int pos) { // pos is a right point . 26 if( ! ( siz[pos] & 1 ) ) return 1; 27 for(int at=rg.start(pos);at;rg.ite(at)) { 28 const int tar = rg.t[at]; 29 if( bel[tar] == pos ) { 30 if( visl[tar] == tim ) continue; 31 visl[tar] = tim; 32 for(int at=g.start(tar);at;g.ite(at)) { 33 --siz[pos] , ++siz[g.t[at]] , bel[tar] = g.t[at]; 34 if( dfs(g.t[at]) ) return 1; 35 ++siz[pos] , --siz[g.t[at]] , bel[tar] = pos; 36 } 37 } else { 38 if( visr[bel[tar]] == tim ) continue; 39 visr[bel[tar]] = tim; 40 const int b = bel[tar]; 41 --siz[b] , ++siz[pos] , bel[tar] = pos; 42 if( dfs(b) ) return 1; 43 ++siz[b] , --siz[pos] , bel[tar] = b; 44 } 45 } 46 return 0; 47 } 48 49 inline void init() { 50 g.reset() , rg.reset(); 51 memset(bel,0,sizeof(bel)) , memset(siz,0,sizeof(siz)) , 52 memset(visl,0,sizeof(visl)) , memset(visr,0,sizeof(visr)); 53 } 54 55 56 int main() { 57 while( scanf("%d%d",&n,&m) == 2 && ( n || m ) ) { 58 int ans = 0; 59 init(); 60 for(int i=1,t,x;i<=m;i++) { 61 scanf("%d",&t); 62 while(t--) { 63 scanf("%d",&x) , g.addedge(x,i) , rg.addedge(i,x); 64 if( !bel[x] ) bel[x] = i , ++siz[i]; 65 } 66 } 67 for(int i=1;i<=m;i++) { 68 visr[i] = tim = i , dfs(i); 69 } 70 for(int i=1;i<=m;i++) ans += ( siz[i] >> 1 ); 71 printf("%d\n",ans); 72 } 73 return 0; 74 }
既然我这么菜的话,还是赶紧滚粗学高考去吧。