牛客NOIP提高组(二)题解
心路历程
预计得分:100 + 40 + 30 = 170
实际得分:100 + 30 + 0 = 130
T2有一个部分分的数组没开够RE了。
T3好像是思路有点小问题。。 思路没问题,实现的时候一个细节没想过来。。
Sol
T1
直接把式子化开,发现都可以$O(1)$维护,做完了。。。
#include<cstdio> #include<algorithm> #include<vector> #define LL long long using namespace std; const LL MAXN = 1e5 + 10; inline LL read() { char c = getchar(); LL x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } LL N, a[MAXN], sum, sump; int main() { N = read(); for(LL i = 1; i <= N; i++) a[i] = read(), sum += a[i], sump += a[i] * a[i]; for(LL i = 1; i <= N; i++) { LL ans = (N - 1) * (sump - a[i] * a[i]) - 2 * (sum - a[i]) * (sum - a[i]) + (sum - a[i]) * (sum - a[i]); if(i != N) printf("%lld ", ans); else printf("%lld", ans); } return 0; }
T2
好难啊,$30$分的枚举颜色dp应该比较好想把,$f[i][j]$表示第$i$个位置,填了$j$个颜色,然后先枚举一下$1$的颜色,前缀和优化一下,$O(n a_i^2)$
正解:
考虑容斥,令$f[i]$表示第$i$个位置的答案
什么都不考虑:$f[i] = f[i - 1] \times a[i]$
这时候会算重第$i$个位置和第$i - 1$个位置相同的情况,减去$f[i-2] * min(a[i], a[i - 1])$
然后会多减去$i - 2, i - 1, i$这三个位置相同的情况,加上$f[i - 3] * min(a[i], a[i -1], a[i - 2])$
不断容斥下去。
$f[i] = \sum_{j} (-1)^{i - j} f[i - j + 1] * min(a_{(i - j) \ to \ i})$
但是还有一个问题:这玩意儿是环形的,还需要考虑$1$和$i$不同的情况,。。。
这就很麻烦了,因为$1$和$i$的高度问题很难讨论。
我们直接把序列转一转,令$1$号位置的元素最小,这样$1$号元素不论取什么,$n$号位置一定会少一种取法
考虑如何优化这玩意儿,稍微化简一下,式子就变成了这样(负变正不影响奇偶性)
$f[i] = (-1)^i \sum_{j} (-1)^j f[i - j +1] \times min(a_{(i - j) \ to \ i})$
把$(-1)^j $和$f$看成一项,这东西显然有单调性。
考虑用单调栈去维护
具体做法是:考虑从$i$转移到$i+1$时答案的变化
一种情况是$a[i+1] > a[i]$这时候直接加上$i+1$的贡献即可
另一种情况是$a[i+1] < a[i]$,这时候前面的一些元素对答案的贡献会减小,用单调栈维护,找到第一个小于等于它的,减去这之间的位置的贡献即可。
时间复杂度:$O(n)$
代码中还用到了一个容斥。上面说的dp是不考虑$1$号位置与$i$号位置相同的情况
设$g_i$表示$1$与$i$拼成环相同时的方案数,稍微观察一下不难发现$g_i = f_{i - 1} - g_{i - 1}$。(把$1$和$i$看成相同的元素)
需要注意的是$g_2 = 0$,因此到达$2$时就需要停止
其实这个容斥方法也可以用来求该问题没有$a_i$限制时的通项公式。
代码较为高能,请谨慎查看。。
/* */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> #include<set> #include<queue> #include<cmath> //#include<ext/pb_ds/assoc_container.hpp> //#include<ext/pb_ds/hash_policy.hpp> #define Pair pair<LL, LL> #define MP(x, y) make_pair(x, y) #define fi first #define se second //#define LL long long #define LL long long #define ull unsigned long long #define rg register #define pt(x) printf("%d ", x); using namespace std; //using namespace __gnu_pbds; const LL MAXN = 1e6 + 10, INF = 1e9 + 10, mod = 1e9 + 7; const double eps = 1e-9; inline LL read() { char c = getchar(); LL x = 0, f = 1; while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } LL N, a[MAXN], b[MAXN], f[MAXN], top; Pair s[MAXN]; LL id(LL x) { return x & 1 ? (-1) : 1; } int main() { N = read(); LL mn = INF, pos = 1, cnt = 0; for(LL i = 1; i <= N; i++) { a[i] = read(); if(a[i] < mn) mn = a[i], pos = i; } for(LL i = 1; i <= N; i++) b[i] = a[pos], pos = pos % N + 1; memcpy(a, b, sizeof(b)); // for(LL i = 1; i <= N; i++) printf("%d ", b[i]); f[0] = -1; /*for(LL i = 1; i <= N; i++) { LL opt = 1, mn = INF; for(LL j = i; j >= 1; j--) { mn = min(a[j], mn); f[i] = (f[i] + opt * f[j - 1] * mn + mod) % mod; opt = (opt == 1 ? -1 : 1); } }*/ LL sum = 0; s[++top] = MP(0, 1);LL ans = 0; for(int i = 1; i <= N; i++) { LL now = 0; while(top && a[i] <= s[top].fi) { sum = (sum - s[top].fi * s[top].se % mod) % mod; now += s[top].se; top--; } now = (now + f[i - 1]) % mod; //sum += id(i) * a[i] * now; f[i] = sum + a[i] * now % mod; if(i & 1) f[i] = -f[i]; f[i] = (f[i] + mod) % mod; if(i != 1) if((N - i) & 1) ans = (ans - f[i] + mod) % mod; else ans = (ans + f[i]) % mod; if((i - 1) & 1) f[i] = -f[i]; f[i] = (f[i] + mod) % mod; s[++top] = MP(a[i], now); (sum = sum + now * a[i] % mod + mod) %= mod; } /*for(int i = N; i >= 2; i--) if((N - i) & 1) ans = (ans - abs(f[i]) + mod) % mod; else ans = (ans + abs(f[i])) % mod; */ //for(LL i = 1; i <= N; i++) printf("%I64d\n", f[i]); //puts(""); cout << (ans + mod) % mod; return 0; } /* 6 987 654 321 87 54 321 1 10 154 542 12 1 4354 2 12 121 2 45 4 3 5 1 2 4 4 4 4 4 3 3 3 3 2 2 3 1 4 */
T3
先考虑$m = 0$时。
定义$lowbit(i)$表示$i$的二进制下第一个为$1$的位置,很显然,如果我们把$lowbit(x)$相等的元素全都分给其中一个玩家这样一定是合法的。
每一个$i$能够贡献的数量为$2^{n - i}$。直接对$k$二进制拆分即可。考场上想到了,但是实现的时候傻乎乎的以为每次拆分的时候二进制位不能有重叠。。
$m \not = 0$的时候好像是个神仙dp,明天再看。。咕咕咕