20180329小测
让大家都AK的信心赛......
话说这种考试有什么意思(不),还不如做我出的那套模拟题呢......
T1:
其实这题是我三道题里AC得最晚的......
首先我们能用bitset+大力背包骗到58-64分......
考虑我们如果先枚举两个数能组合出的值,再计算另外一个数的方案数,什么情况下会算重?
显然是当这个值取模最后的那个数相同的时候,这样一个方案数会被计算多次!
此外还有一个十分显然的结论:当两个值取模最后那个数相同时,较大的那个值的答案会被包含在较小的那个值的答里面。
这样的话,我们在取模abc中某一个数的同余系下跑一个最短路,用另外两个数做边进行转移,最后再统计一发答案不就好了。
60分bitset代码:
1 #pragma GCC optimize(3) 2 #include<cstdio> 3 #include<bitset> 4 const int maxn=1e8+1e2; 5 std::bitset<maxn> vis; 6 7 long long h; 8 int a,b,c,ans; 9 10 11 int main() { 12 scanf("%lld%d%d%d",&h,&a,&b,&c); 13 if( a == 1 || b == 1 || c == 1 ) return printf("%lld\n",h),0; 14 for(int i=0;a*i<h;i++) { 15 if( vis[a*i] ) break; 16 for(int j=0;a*i+b*j<h;j++) { 17 if( vis[a*i+b*j] ) break; 18 for(int k=0;a*i+b*j+k*c<h;k++) { 19 if( vis[a*i+b*j+k*c] ) break; 20 vis[a*i+b*j+k*c] = 1; 21 } 22 } 23 } 24 printf("%llu\n",vis.count()); 25 return 0; 26 }
正解代码:
1 #include<bits/stdc++.h> 2 #define bool unsigned char 3 typedef long long int lli; 4 using namespace std; 5 const int maxn=1e5+1e2; 6 7 lli h,ans; 8 lli dis[maxn]; 9 int s[maxn],t[maxn<<4],nxt[maxn<<4],l[maxn<<4]; 10 bool inq[maxn]; 11 int a,b,c; 12 13 inline void addedge(int from,int to,int len) { 14 static int cnt = 0; 15 t[++cnt] = to , l[cnt] = len , 16 nxt[cnt] = s[from] , s[from] = cnt; 17 } 18 inline void spfa(int st) { 19 memset(dis,0x7f,sizeof(dis)) , dis[st] = 0; 20 queue<int> q; q.push(st) , inq[st] = 1; 21 while( q.size() ) { 22 const int pos = q.front(); q.pop() , inq[pos] = 0; 23 for(int at=s[pos];at;at=nxt[at]) 24 if( dis[t[at]] > dis[pos] + l[at] ) { 25 dis[t[at]] = dis[pos] + l[at]; 26 if( !inq[t[at]] ) q.push(t[at]) , inq[t[at]] = 1; 27 } 28 } 29 } 30 inline void getans() { 31 --h; 32 for(int i=0;i<c;i++) 33 if( dis[i] <= h ) 34 ans += ( h - dis[i] ) / c + 1; 35 } 36 37 int main() { 38 scanf("%lld%d%d%d",&h,&a,&b,&c); 39 if( a > b ) swap(a,b); 40 if( b > c ) swap(b,c); 41 for(int i=0;i<c;i++) 42 addedge(i,(i+a)%c,a) , addedge(i,(i+b)%c,b); 43 spfa(0); 44 getans(); 45 printf("%lld\n",ans); 46 return 0; 47 }
T2:
怕不是stl:rope裸题......
rope能实现区间提取,单点插入区间,正好能用来做这道题......
代码:
1 #include<iostream> 2 #include<ext/rope> 3 const int maxn=2.5e5+1e2; 4 5 char in[maxn]; 6 __gnu_cxx::rope<char> str,ans; 7 8 int main() { 9 static char com[10]; 10 static int k,len; 11 std::ios::sync_with_stdio(0) , std::cin.tie(0) , std::cout.tie(0); 12 std::cin >> in , str = in; 13 while( ( std::cin >> com ) && *com != 'E' ) { 14 if( *com == 'I' ) { 15 std::cin >> in >> k; 16 str.insert(k,in); 17 } else if( *com == 'P' ) { 18 std::cin >> k >> len; 19 ans = str.substr(k,len-k+1); 20 std::cout << ans << std::endl; 21 } 22 } 23 return 0; 24 }
T3:
如果只是求和应该怎样做?一个很显然的换根树状DP。
现在要求k次呢?这样的话距离+1-1就需要进行变化了。
我们维护x^0~x^k的一个多项式,在更新x^k的时候利用二项式定理和更低次的项进行更新就好了。
这样一次更新复杂度O(k),总复杂度O(nkt),稳稳地AC了。
根节点深度为1好评,省去了对于0^k等问题的繁琐的分类讨论......
代码:
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=2e4+1e2,maxk=25; 9 const int mod=1e9+7; 10 11 12 lli c[maxk][maxk]; 13 int n,k; 14 15 namespace Pre { 16 lli fac[maxk],inv[maxk]; 17 inline lli fastpow(lli base,int tim) { 18 lli ret = 1; 19 while( tim ) { 20 if( tim & 1 ) ret = ret * base % mod; 21 if( tim >>= 1 ) base = base * base % mod; 22 } 23 return ret; 24 } 25 inline lli C(int n,int m) { 26 return fac[n] * inv[m] % mod * inv[n-m] % mod; 27 } 28 inline void work() { 29 *fac = 1; 30 for(int i=1;i<=k;i++) fac[i] = fac[i-1] * i % mod; 31 inv[k] = fastpow(fac[k],mod-2); 32 for(int i=k;i;i--) inv[i-1] = inv[i] * i % mod; 33 for(int i=0;i<=k;i++) 34 for(int j=0;j<=i;j++) 35 c[i][j] = C(i,j); 36 } 37 } 38 39 struct Poly { 40 lli dat[maxk]; 41 Poly() { 42 memset(dat,0,sizeof(dat)); 43 } 44 lli& operator [] (const int &x) { 45 return dat[x]; 46 } 47 const lli& operator [] (const int &x) const { 48 return dat[x]; 49 } 50 friend Poly operator + (const Poly &a,const Poly &b) { 51 Poly ret; 52 for(int i=0;i<=k;i++) 53 ret[i] = ( a[i] + b[i] ) % mod; 54 return ret; 55 } 56 friend Poly operator - (const Poly &a,const Poly &b) { 57 Poly ret; 58 for(int i=0;i<=k;i++) 59 ret[i] = ( a[i] - b[i] + mod ) % mod; 60 return ret; 61 } 62 friend Poly operator + (const Poly &a,lli x) { 63 Poly ret; x = ( x + mod ) % mod; 64 static lli pows[maxk]; 65 *pows = 1; 66 for(int i=1;i<=k;i++) pows[i] = pows[i-1] * x % mod; 67 for(int i=k;~i;i--) { 68 for(int j=i;~j;j--) 69 ret[i] += a[j] * pows[i-j] % mod * c[i][j] % mod , ret[i] %= mod; 70 } 71 return ret; 72 } 73 inline void init() { 74 for(int i=0;i<=k;i++) dat[i] = 1; 75 } 76 }sson[maxn],sfa[maxn],f[maxn]; 77 // sson is dis of points in subtree to pos . 78 79 int s[maxn],t[maxn<<1],nxt[maxn<<1],fa[maxn],cnt; 80 81 inline void addedge(int from,int to) { 82 t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt; 83 } 84 inline void dfsson(int pos) { 85 sson[pos].init(); 86 for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa[pos] ) { 87 fa[t[at]] = pos , dfsson(t[at]); 88 sson[pos] = sson[pos] + ( sson[t[at]] + 1 ) ; 89 } 90 } 91 inline void dfsfa(int pos) { 92 if( fa[pos] ) { 93 sfa[pos] = sfa[fa[pos]] + 1; 94 Poly delta = sson[fa[pos]] - ( sson[pos] + 1 ); 95 sfa[pos] = sfa[pos] + ( delta + 1 ); 96 } 97 for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa[pos] ) dfsfa(t[at]); 98 } 99 inline void getans() { 100 for(int i=1;i<=n;i++) { 101 Poly sum = sfa[i] + sson[i]; 102 printf("%lld\n",sum.dat[k]); 103 } 104 putchar('\n'); 105 } 106 107 int main() { 108 while( scanf("%d%d",&n,&k) == 2 ) { 109 memset(s,0,sizeof(int)*(n+1)) , cnt = 0; 110 Pre::work(); 111 for(int i=1,a,b;i<n;i++) { 112 scanf("%d%d",&a,&b) , 113 addedge(a,b) , addedge(b,a); 114 } 115 dfsson(1) , dfsfa(1); 116 getans(); 117 } 118 return 0; 119 }
最后上排名。
(为什么3个AK的?因为有一个是我用我老婆的名字注册的账号(四蒸心.jpg))
话说比赛还没结束呢就发题解真的好吗?
另外我的桌子竟然带电!准备报警了!