离NOIP2018越来越近了,然而我仍有一些没有彻底弄懂的地方,有点慌,所以准备从这周开搞搞基础。
不说别的了,先把这篇总结写完。
Day1的题感觉不是很难,每一道题的暴力也都可写,总的来说难度跟noip差不多吧。
有一个想吐槽的地方,就是为啥题目把算法都告诉了……
T1 backpack
期望得分:100
实际得分:100
正像题解所说,Day1T1是送分的。不过辛亏我以前做过一道类似的题,要不然只能得60。
首先40分是送的,完全背包板子。然后发现尽管物品很多,但是体积和价值只有100。所以如果两件物品体积相同,那么就取价值更大的。所以实际上物品最多只有100个。然后一个O(100 * n)就得了60分。
正解其实我也不知道为啥,不过以前做过一道搜索题,那道题数据范围特别大,但是除了暴力我实在想不出来该咋办,然后看题解时记住了这么一句话:“远距离贪心,近距离暴力”。到了这道题,就变成了“大容量贪心,小容量背包”。于是我们用性价比最高的物品填背包,直到剩余容量在1e5范围内,然后正常完全背包了。
考场代码
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxm = 1e5 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 void MYFILE() 37 { 38 #ifndef mrclr 39 freopen("backpack.in", "r", stdin); 40 freopen("backpack.out", "w", stdout); 41 #endif 42 } 43 44 int n; 45 ll m; 46 ll dp[maxm]; 47 int t[105]; 48 49 void work0() 50 { 51 ll ans = 0; 52 int pos = 1; 53 for(int i = 1; i <= 100; ++i) if((ll)t[i] * (ll)pos > (ll)t[pos] * (ll)i) pos = i; 54 ll x = (m - maxm + (ll)pos - 1) / (ll)pos; 55 m -= x * (ll)pos; 56 ans += x * (ll)t[pos]; 57 // printf("@%lld ", ans); 58 for(int i = 1; i <= 100; ++i) 59 for(int j = i; j <= m; ++j) 60 dp[j] = max(dp[j], dp[j - i] + t[i]); 61 write(dp[m] + ans), enter; 62 } 63 64 int main() 65 { 66 MYFILE(); 67 n = read(); m = read(); 68 for(int i = 1; i <= n; ++i) 69 { 70 int x = read(), y = read(); 71 t[x] = max(t[x], y); 72 } 73 if(m > maxm) {work0(); return 0;} 74 for(int i = 1; i <= 100; ++i) 75 for(int j = i; j <= m; ++j) 76 dp[j] = max(dp[j], dp[j - i] + t[i]); 77 // for(int j = 1; j <= m; ++j) printf("%lld ", dp[j]); enter; 78 write(dp[m]), enter; 79 return 0; 80 } 81 /* 82 2 15 83 3 2 84 5 3 85 */
T2 sort
期望得分:100
实际得分:100
讲道理T2看了半天才把题看懂,因为我一直认为si可以等于tj,然后方案数总算不对。换然后看到题面说把{ci}分离成这两个序列,才觉得不对劲。如果对于任何 i和 j,都有si != sj ,那么样例就解释明白了:最大值一定是取sum[mid +1, r] - sum[l, mid];最小值一定是si和ti隔一个取一个。然后有这么个规律:对于当前的一个点x,si已经取到的数量一定大于等于ti取得数量。这个规律不就是括号匹配的方案数吗!答案就是卡特兰数呀。
然后我就忘了卡特兰数的递推式了。只记得有一个C(n / 2, n),于是现场2n打表前10项,一顿神推竟然推出来了。不过忘了线性求逆元,于是O(nlogn)预处理。辛亏1e6没卡我这个。
剩下的就是线段树维护区间和,奇偶和了。基本跟普通的线段树一样,不过虽然题目保证区间长度是偶数,但是线段树分治下去的不一定是奇数,所以pushdown的时候还要判断一下是奇数还是偶数累加的值比区间和的一半多1个,多了得补上。
结果刚开始我没注意这个,以为是建树不对,又重写了一遍,就过更不对了。回头又改这个,这样就耽误了不少时间,导致T3没有多少时间了。
还是考场代码
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const ll mod = 1e9 + 7; 21 const int maxn = 1e6 + 5; 22 inline ll read() 23 { 24 ll ans = 0; 25 char ch = getchar(), last = ' '; 26 while(!isdigit(ch)) {last = ch; ch = getchar();} 27 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 28 if(last == '-') ans = -ans; 29 return ans; 30 } 31 inline void write(ll x) 32 { 33 if(x < 0) x = -x, putchar('-'); 34 if(x >= 10) write(x / 10); 35 putchar(x % 10 + '0'); 36 } 37 void MYFILE() 38 { 39 #ifndef mrclr 40 freopen("sort.in", "r", stdin); 41 freopen("sort.out", "w", stdout); 42 #endif 43 } 44 45 int n, m; 46 47 ll quickpow(ll a, ll b) 48 { 49 a %= mod; 50 ll ret = 1; 51 while(b) 52 { 53 if(b & 1) ret = ret * a % mod; 54 a = a * a % mod; b >>= 1; 55 } 56 return ret; 57 } 58 ll fac[maxn], inv[maxn], cat[maxn]; 59 inline void init(const int& N) 60 { 61 fac[1] = 1; 62 for(int i = 2; i <= N; ++i) fac[i] = fac[i - 1] * i % mod; 63 inv[N] = quickpow(fac[N], mod - 2); 64 for(int i = N - 1; i; --i) inv[i] = inv[i + 1] * (i + 1) % mod; 65 ll x = 1; 66 for(int i = 2; i <= N; i += 2) 67 { 68 x++; 69 cat[i >> 1] = fac[i] * inv[i >> 1] % mod * inv[i >> 1] % mod * quickpow(x, mod - 2) % mod; 70 } 71 } 72 73 int l[maxn << 2], r[maxn << 2]; 74 ll sum[maxn << 2], sum1[maxn << 2], sum0[maxn << 2], lzy[maxn << 2]; 75 void pushup(int now) 76 { 77 sum[now] = sum[now << 1] + sum[now << 1 | 1]; sum[now] %= mod; 78 sum1[now] = sum1[now << 1] + sum1[now << 1 | 1]; sum1[now] %= mod; 79 sum0[now] = sum0[now << 1] + sum0[now << 1 | 1]; sum0[now] %= mod; 80 } 81 void build(int L, int R, int now) 82 { 83 l[now] = L; r[now] = R; 84 if(L == R) 85 { 86 sum[now] = read(); sum[now] %= mod; 87 if(L & 1) sum1[now] = sum[now]; 88 else sum0[now] = sum[now]; 89 return; 90 } 91 int mid = (L + R) >> 1; 92 build(L, mid, now << 1); 93 build(mid + 1, R, now << 1 | 1); 94 pushup(now); 95 } 96 void pushdown(int now) 97 { 98 if(lzy[now]) 99 { 100 ll ha1 = (ll)(r[now << 1] - l[now << 1] + 1) * lzy[now]; 101 sum[now << 1] += ha1; sum[now << 1] %= mod; 102 if((r[now << 1] - l[now << 1] + 1) & 1) 103 { 104 if(l[now << 1] & 1) sum1[now << 1] += lzy[now]; 105 else sum0[now << 1] += lzy[now]; 106 } 107 // if(l[now << 1] != r[now << 1] || (l[now << 1] == r[now << 1] && (l[now << 1] & 1))) 108 sum1[now << 1] += (ha1 >> 1); sum1[now << 1] %= mod; 109 // if(l[now << 1] != r[now << 1] || (l[now << 1] == r[now << 1] && !(l[now << 1] & 1))) 110 sum0[now << 1] += (ha1 >> 1); sum0[now << 1] %= mod; 111 ll ha2 = (ll)(r[now << 1 | 1] - l[now << 1 | 1] + 1) * lzy[now]; 112 sum[now << 1 | 1] += ha2; sum[now << 1 | 1] %= mod; 113 if((r[now << 1 | 1] - l[now << 1 | 1] + 1) & 1) 114 { 115 if(l[now << 1 | 1] & 1) sum1[now << 1 | 1] += lzy[now]; 116 else sum0[now << 1 | 1] += lzy[now]; 117 } 118 // if(l[now << 1 | 1] != r[now << 1 | 1] || (l[now << 1 | 1] == r[now << 1 | 1] && (l[now << 1 | 1] & 1))) 119 sum1[now << 1 | 1] += (ha2 >> 1); sum1[now << 1 | 1] %= mod; 120 // if(l[now << 1 | 1] != r[now << 1 | 1] || (l[now << 1 | 1] == r[now << 1 | 1] && !(l[now << 1 | 1] & 1))) 121 sum0[now << 1 | 1] += (ha2 >> 1); sum0[now << 1 | 1] %= mod; 122 lzy[now << 1] += lzy[now]; lzy[now << 1] %= mod; 123 lzy[now << 1 | 1] += lzy[now]; lzy[now << 1] %= mod; 124 lzy[now] = 0; 125 } 126 } 127 void update(int L, int R, int now, int d) 128 { 129 if(L == l[now] && R == r[now]) 130 { 131 ll ha = (ll)(R - L + 1) * d; 132 sum[now] += ha; sum[now] %= mod; 133 if((R - L + 1) & 1) 134 { 135 if(L & 1) sum1[now] += d; 136 else sum0[now] += d; 137 } 138 sum1[now] += (ha >> 1); sum1[now] %= mod; 139 sum0[now] += (ha >> 1); sum0[now] %= mod; 140 lzy[now] += d; lzy[now] %= mod; 141 return; 142 } 143 pushdown(now); 144 int mid = (l[now] + r[now]) >> 1; 145 if(R <= mid) update(L, R, now << 1, d); 146 else if(L > mid) update(L, R, now << 1 | 1, d); 147 else update(L, mid, now << 1, d), update(mid + 1, R, now << 1 | 1, d); 148 pushup(now); 149 } 150 ll query(int L, int R, int now, int flg) 151 { 152 if(L == l[now] && R == r[now]) 153 { 154 if(flg == 0) return sum0[now]; 155 else if(flg == 1) return sum1[now]; 156 else return sum[now]; 157 } 158 pushdown(now); 159 int mid = (l[now] + r[now]) >> 1; 160 if(R <= mid) return query(L, R, now << 1, flg); 161 else if(L > mid) return query(L, R, now << 1 | 1, flg); 162 else return (query(L, mid, now << 1, flg) + query(mid + 1, R, now << 1 | 1, flg)) % mod; 163 } 164 165 int main() 166 { 167 MYFILE(); 168 n = read(); m = read(); 169 init(n << 1); 170 build(1, n << 1, 1); 171 for(int i = 1; i <= m; ++i) 172 { 173 int d = read(), L = read(), R = read(); 174 if(d == 0) 175 { 176 int val = read(); 177 update(L, R, 1, val); 178 } 179 else 180 { 181 int mid = (L + R) >> 1; 182 write((query(mid + 1, R, 1, 2) - query(L, mid, 1, 2) + mod) % mod), space; 183 ll Min = query(L, R, 1, 0), Max = query(L, R, 1, 1); 184 if(L & 1) swap(Min, Max); 185 write((Max - Min + mod) % mod), space; 186 write(cat[(R - L + 1) >> 1]), enter; 187 } 188 } 189 return 0; 190 }
T3 digit
期望得分:0
实际得分:0
其实写暴力的时间还是有的,剩了40多分钟。主要原因是题没看懂:数位和到底是什么玩意儿!?样例模拟了半天还是不对,剩20多分钟的时候就放弃了,因为这点时间写生什么都来不及了,还不如检查T1,T2。
现在发现T3部分分其实特别好拿。数位和就是这个数每一位上的数的和。所以30分暴力只要枚举k进制下L位到R位的所有数即可。
50分是一个比较水的暴dp。题目数位dp纯粹和唬你的。因为4, 5, 6的最小公倍数是60,所以任何大于60的数只要对60取模就行了。令dp[i][j]表示到第 i 位,数位和 mod 60等于 j 的数的个数。那么就有dp[i][j] = Σdp[i - 1][j - h] (0 <= h < k)。因为j - h可能为负,所以代码应该写成(j - h % 60 + 60) % 60。最后的答案用前缀和的思想:ans = Σdp[R][i] - Σdp[L - 1][i],i 满足能被4,5,6其中一个整除。
初始化应该是for(i : 0 ~k - 1) dp[1][i % 60]++。刚开始写成了dp[1][i] = 1。只得了20分,debug可好半天。
满分做法:用矩乘优化。推转移矩阵的时候磨叽了好半天(还是不熟练,没理解透),最后还是看了std的代码。然后跑两遍快速幂。
60分代码
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const ll mod = 1e9 + 7; 21 const int maxn = 1e3 + 5; 22 inline ll read() 23 { 24 ll ans = 0; 25 char ch = getchar(), last = ' '; 26 while(!isdigit(ch)) {last = ch; ch = getchar();} 27 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 28 if(last == '-') ans = -ans; 29 return ans; 30 } 31 inline void write(ll x) 32 { 33 if(x < 0) x = -x, putchar('-'); 34 if(x >= 10) write(x / 10); 35 putchar(x % 10 + '0'); 36 } 37 void MYFILE() 38 { 39 #ifndef mrclr 40 freopen("digit.in", "r", stdin); 41 freopen("digit.out", "w", stdout); 42 #endif 43 } 44 45 ll l, r, k; 46 ll dp[maxn][65], ans = 0; 47 48 int main() 49 { 50 MYFILE(); 51 l = read(); r = read(); k = read(); 52 if(r > 1000) return 0; 53 for(rg int i = 0; i < k; ++i) dp[1][i % 60]++; 54 for(rg int i = 2; i <= r; ++i) 55 for(rg int j = 0; j < 60; ++j) 56 for(rg int h = 0; h < k; ++h) 57 dp[i][j] += dp[i - 1][(j - h % 60 + 60) % 60], dp[i][j] %= mod; 58 for(rg int i = 0; i < 60; ++i) 59 if(!(i % 4) || !(i % 5) || !(i % 6)) ans += dp[r][i] - dp[l - 1][i], ans %= mod; 60 write((ans - (l == 1) + mod) % mod); enter; 61 return 0; 62 }
100分代码
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const ll mod = 1e9 + 7; 21 const int N = 60; 22 inline ll read() 23 { 24 ll ans = 0; 25 char ch = getchar(), last = ' '; 26 while(!isdigit(ch)) {last = ch; ch = getchar();} 27 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 28 if(last == '-') ans = -ans; 29 return ans; 30 } 31 inline void write(ll x) 32 { 33 if(x < 0) x = -x, putchar('-'); 34 if(x >= 10) write(x / 10); 35 putchar(x % 10 + '0'); 36 } 37 void MYFILE() 38 { 39 #ifndef mrclr 40 freopen("digit.in", "r", stdin); 41 freopen("digit.out", "w", stdout); 42 #endif 43 } 44 45 ll l, r, k, ans = 0; 46 47 struct Mat 48 { 49 ll a[N + 5][N + 5]; 50 Mat operator * (const Mat &oth)const 51 { 52 Mat ret; Mem(ret.a, 0); 53 for(int i = 0; i < N; ++i) 54 for(int j = 0; j < N; ++j) 55 for(int k = 0; k < N; ++k) 56 ret.a[i][j] = (ret.a[i][j] + a[i][k] * oth.a[k][j]) % mod; 57 return ret; 58 } 59 }f; 60 61 Mat quickpow(Mat A, ll b) 62 { 63 Mat ret; Mem(ret.a, 0); 64 for(int i = 0; i < 60; ++i) ret.a[i][i] = 1; 65 while(b) 66 { 67 if(b & 1) ret = ret * A; 68 A = A * A; b >>= 1; 69 } 70 return ret; 71 } 72 73 int main() 74 { 75 MYFILE(); 76 ll l = read(), r = read(), k = read(); 77 Mem(f.a, 0); 78 for(int i = 0; i < N; ++i) 79 { 80 for(int j = 0; j < N; ++j) 81 f.a[i][j] = k / N % mod; 82 for(int j = 0; j < k % N; ++j) 83 f.a[i][(i + j) % N]++, f.a[i][(i + j) % N] %= mod; 84 } 85 Mat A = quickpow(f, r); 86 for(int i = 0; i < N; ++i) 87 if(!(i % 4) || !(i % 5) || !(i % 6)) ans = (ans + A.a[0][i]) % mod; 88 A = quickpow(f, l - 1); 89 for(int i = 0; i < N; ++i) 90 if(!(i % 4) || !(i % 5) || !(i % 6)) ans = (ans - A.a[0][i] + mod) % mod; 91 write(ans), enter; 92 return 0; 93 }
Day1美中不足的地方在于T2花费的时间有点多,而这是因为没有思考周全就开始写代码了,写完后就没有自信,然后有点盲目的改,结果浪费了不少时间。