北京集训: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 }
View Code

 

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 }
View Code


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 }
View Code

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 }
View Code


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 }
View Code

乱搞代码:

 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 }
View Code


既然我这么菜的话,还是赶紧滚粗学高考去吧。

posted @ 2018-03-14 21:18  Cmd2001  阅读(137)  评论(0编辑  收藏  举报