連接python
by Yuki & Asm.Defc++
指望難度:Hard-git
考慮從後往前進行博弈動態規劃,在這一過程當中維護全部的先手必勝區間。區間不妨採用左開右閉,方便轉移。算法
考慮一次轉移,若是當前Servant的後一個位置屬於對手,則當前Servant的必勝區間能夠經過將後一個Servant的每一個必敗區間的左端點+一、右端點+x獲得;若是後一個位置屬於本身,則能夠經過將後一個Servant的必勝區間作一樣的操做獲得。不妨分別對必勝區間左右端點維護一個偏移量,須要從對手進行轉移時只需修改偏移量後交換左右端點的集合,而後再在左端點的集合裏插入一個0便可。express
須要注意的是,這樣獲得的必勝區間會有重疊,可能致使對下一個對手的必勝區間的統計出錯。考慮到每次轉移時全部的同類區間的長度的變化量都相同,能夠分別用兩個優先隊列維護這兩類區間,每次轉移後暴力合併重疊的區間便可。數組
複雜度\(O((A+B) \log(A+B))\)electron
//Asm.Def #include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn = 100005; int A, B, M; LL N, x; bool p[maxn], beg_A; void init() { scanf("%lld%lld%d%d", &N, &x, &A, &B); assert(A+B <= 100000); assert(A+B >= N); assert(A >= 1 && B >= 1); M = A+B; for(int i = 1;i <= M;++i) p[i] = false; int a; beg_A = false; for(int i = 0;i < A;++i) { scanf("%d", &a); p[a] = 1; if(a == 1) beg_A = true; } } int a[maxn], len; struct Seg { int l, r; LL len; }; bool operator < (const Seg &a, const Seg &b) { return a.len > b.len; } void work() { len = 0; int cnt = 0; for(int i = 1;i <= M;++i) { ++cnt; if(i == M || p[i+1] != p[i]) { a[len++] = cnt; cnt = 0; } } bool ans = false; static LL loc[maxn]; static int nxt[maxn], lst[maxn]; static bool sel[maxn]; priority_queue<Seg> Q[2]; bool L = 0, R = 1; LL offs[2] = {0}; loc[len] = 0; nxt[len] = lst[len] = len;//用鏈表記錄當前區間端點 sel[len] = false; for(int i = len-1;i >= 0;--i) { //區間總體右移 offs[L] += a[i] * x; offs[R] += a[i]; L ^= 1, R ^= 1; //插入新增的左端點 loc[i] = -offs[L]; nxt[i] = nxt[len]; lst[nxt[i]] = i; lst[i] = len; nxt[len] = i; sel[i] = true; Q[L].push( (Seg){i, nxt[i], loc[nxt[i]]-loc[i]} ); //合併區間 LL t = offs[R] - offs[L];//R(k)+offs[R] < L(k+1)+offs[L] Seg tmp; while(!Q[R].empty() && ((tmp = Q[R].top()).len <= t || !sel[tmp.l] || !sel[tmp.r]) ) { Q[R].pop(); if(sel[tmp.l] && sel[tmp.r])//合併一對跨立的"(](]", { sel[tmp.l] = sel[tmp.r] = false; nxt[lst[tmp.l]] = nxt[tmp.r]; lst[nxt[tmp.r]] = lst[tmp.l]; Q[L].push( (Seg){lst[tmp.l], nxt[tmp.r], loc[nxt[tmp.r]]-loc[lst[tmp.l]]} ); } //不然爲已被合併的區間,無需處理 } } int it = nxt[len]; ans = false; while(it != len && loc[it]+offs[L] < N) { if(nxt[it] == N || loc[nxt[it]]+offs[R] >= N) { ans = true; break; } it = nxt[it]; if(it != len) it = nxt[it]; if(it == 0) break; } puts((ans ^ beg_A) ? "Ritsuka" : "Gudako"); } int main() { clock_t beg = clock(); int T; scanf("%d", &T); while(T--) { init(); work(); } //printf("%.3f sec\n", double(clock()-beg) / CLOCKS_PER_SEC); return 0; }
連接編碼
指望難度:Mediumspa
表達式求值問題能夠將中綴表達式轉換爲後綴表達式,其中轉換步驟使用調度場算法 與後綴表達式的求值 均在維基百科中有詳細的介紹。code
根據d運算的描述,\(x\ {\rm d}\ y\)本質上是一個定義在整數集的冪集上的運算,即\(對於任意 S_1, S_2 \in \mathbb{2^Z}且 \min(S_1) \geq 0 且 \min(S_2)\geq 1\),定義\(S_1\ {\rm d}\ S_2 = \{x\in \mathbb{Z} \mid \exists a\in S_1, \exists b\in S_2, a\leq x \leq ab\}\). 則對於任意一次有定義的\({\rm d}\)運算,都有$\min(S_1 {\rm d} S_2) = \min(S_1) $, \(\max(S_1\ {\rm d}\ S_2)=\max(S_1)\cdot \max(S_2)\). 其中\(\min(S)\)和\(\max(S)\)分別表示集合\(S\)中的最小值和最大值。
再將其他三種運算擴展到\(\mathbb{2^Z}\)上,可得
\(\min(S_1 + S_2)=\min(S_1)+\min(S_2), \max(S_1+S_2)=\max(S_1)+\max(S_2)\)
\(\min(S_1-S_2) = \min(S_1)-\max(S_2), \max(S_1-S_2) = \max(S_1) - \min(S_2)\)
\(\min(S_1*S_2)=\min\{\min(S_1)*\min(S_2),\ \min(S_1)*\max(S_2),\ \max(S_1) * \min(S_2),\ \max(S_1)*\max(S_2)\}\)
\(\max(S_1*S_2)=\max\{\min(S_1)*\min(S_2),\ \min(S_1)*\max(S_2),\ \max(S_1) * \min(S_2),\ \max(S_1)*\max(S_2)\}\)
因爲咱們只關注最大和最小的結果,因此能夠直接用二元組\(<\min(S), \max(S)>\)來表示一個子表達式的運算結果,按照上述擴展定義進行運算便可。
p.s.雖然從實際意義來看d運算不知足結合律,但若是隻考慮二元組\(<\min(S), \max(S)>\)的話,有\(<\min(S_1\ {\rm d}\ S_2\ {\rm d}\ \cdots\ {\rm d}\ S_n),\ \max(S_1\ {\rm d}\ S_2\ {\rm d}\ \cdots\ {\rm d}\ S_n)>\ =\ <\min(S_1),\ \max(S_1) \max(S_2) \cdots \max(S_n)>\),是無需考慮\({\rm d}\)運算的結合順序的。
#include <bits/stdc++.h> using namespace std; struct Interval { int L, R; Interval(){} Interval(int a, int b) : L(a), R(b) {} void Print(){printf("[%d,%d]\n", L, R);} }; Interval operator + (const Interval &a, const Interval &b) { return Interval(a.L + b.L, a.R + b.R); } Interval operator - (const Interval &a, const Interval &b) { return Interval(a.L - b.R, a.R - b.L); } Interval operator * (const Interval &a, const Interval &b) { int mn = min(min(a.L * b.L, a.L * b.R), min(a.R * b.L, a.R * b.R)); int mx = max(max(a.L * b.L, a.L * b.R), max(a.R * b.L, a.R * b.R)); return Interval(mn, mx); } Interval f(const Interval &a, const Interval &b) { assert(a.L >= 0 && b.L >= 1); return Interval(a.L, a.R * b.R); } int RPNLen, RPNNumLen; Interval RPNNum[105]; char s[105], RPN[105]; inline int precedence(char ope) { if (ope == '+') return 1; if (ope == '-') return 1; if (ope == '*') return 2; if (ope == '/') return 2; if (ope == 'd') return 3; return 0; } void expressionToRPN() { int stackLen = 0, x = 0; char stack[105]; RPNLen = 0; RPNNumLen = 0; for (int i = 0; s[i] != '\0'; i++) { if (s[i] >= '0' && s[i] <= '9') { if (i == 0 || s[i-1] < '0' || s[i-1] > '9') { RPNLen++; RPN[RPNLen] = 'N'; x = s[i] - '0'; } else { x = x * 10 - '0' + s[i]; } if(s[i+1] < '0' || s[i+1] > '9') RPNNum[++RPNNumLen] = Interval(x, x); } else if (s[i] == '(') { stackLen++; stack[stackLen] = s[i]; } else if (s[i] == ')') { while (stack[stackLen] != '(') { RPNLen++; RPN[RPNLen] = stack[stackLen]; stackLen--; } stackLen--; } else { while (stackLen > 0 && precedence(s[i]) <= precedence(stack[stackLen])) { RPNLen++; RPN[RPNLen] = stack[stackLen]; stackLen--; } stackLen++; stack[stackLen] = s[i]; } } while (stackLen > 0) { RPNLen++; RPN[RPNLen] = stack[stackLen]; stackLen--; } } Interval CalcRPN() { int RPNNumCnt = 0, stackLen = 0; Interval stack[105]; for (int i = 1; i <= RPNLen; i++) { if (RPN[i] == 'N') { RPNNumCnt++; stackLen++; stack[stackLen] = RPNNum[RPNNumCnt]; } else { Interval b = stack[stackLen]; stackLen--; Interval a = stack[stackLen]; stackLen--; Interval result; //printf("%c\n", RPN[i]); //a.Print(), b.Print(); if(RPN[i] == '+') result = a + b; if(RPN[i] == '-') result = a - b; if(RPN[i] == '*') result = a * b; if(RPN[i] == 'd') result = f(a, b); //result.Print(); stackLen++; stack[stackLen] = result; } } return stack[stackLen]; } int main() { while (scanf("%s", s) == 1) { expressionToRPN(); Interval ans = CalcRPN(); printf("%d %d\n", ans.L, ans.R); } return 0; }
附對拍用的std.py
# Haizs import re class Interval: def __init__(self, l, r): self.l = l self.r = r def __str__(self): return "%d %d" % (self.l, self.r) def __add__(self, b): return Interval(self.l + b.l, self.r + b.r) def __sub__(self, b): return Interval(self.l - b.r, self.r - b.l) def __mul__(self, b): mn = min(min(self.l*b.l, self.r*b.r), min(self.l*b.r, self.r*b.l)) mx = max(max(self.l*b.l, self.r*b.r), max(self.l*b.r, self.r*b.l)) return Interval(mn, mx) def __pow__(self, b): return Interval(self.l, self.r * b.r) a = input() while a: a = a.replace("d", "**") b = re.sub(r"(\d+)", r"Interval(\1,\1)", a) # print(b) print(eval(b)) try: a = input() except: break
by catsworld & Asm.Def
指望難度:Medium
不考慮外層循環的狀況,那麼答案顯然是:
\[ ans = \sum_{i=1}^{\sqrt{x}} \mu(i) * \frac{1}{6}(\frac{x}{i^2}+1) * (\frac{x}{i^2}) * (2(\frac{x}{i^2}))+1) * i^4 \]
在加了外層循環的狀況下,考慮計算\(\mu(i) * i^4\)的係數
令\(sum(i)=\sum_{j=1}^i j^2\)
對於每個\(\mu(i) * i^4\),它在所有的答案中出現次數爲\(n-i^2+1\)次,能夠推出係數爲\(\sum_{j=i^2}^n sum(\frac{j}{i^2})\)
令\(Max=\frac{n}{i^2}\)
考慮sum括號中的取值,能夠發現,必定有\(i*i個1,i*i個2...i*i個Max-1,(n-i*i+11-(Max-1)*i*i)個Max\)
因此,最終的係數爲
\[ \begin{aligned} &i*i*(\sum_{j=1}^{Max-1}sum(j)) + (n-i*i+1-(Max-1)*i*i)*sum(Max)\\ = &Max*Max*(Max+1)*(Max-1)/12+(n-i*i+1-(Max-1)*i*i)*sum(Max) \end{aligned} \]
考慮到模數很大,計算過程當中須要用相似於分治乘法的思路或int128。
By WYJ2015
答案可轉化爲
\[ \sum_{i=1}^n gay(i) \cdot (n+1-i) \mod p \]
在\(\sum\limits_{i=1}^{n} gay(i) (n+1-i)\)中,\(i^2 \cdot (n+1-i)\)被計入答案當且僅當i不含有平方因子。不妨考慮對全部i的因子進行容斥,即
\[ \begin{aligned} Ans &= \sum_{x=1}^{[\sqrt{n}]} \mu(x) \sum_{k=1}^{[\frac{n}{x^2}]} (k x^2)^2 * (n+1-k x^2) \mod p\\ &= \sum_{x=1}^{[\sqrt{n}]} \mu(x) \left( (n+1)x^4 \sum_{k=1}^{[\frac{n}{x^2}]} k^2 - x^6 \sum_{k=1}^{[\frac{n}{x^2}]} k^3 \right) \mod p\\ \end{aligned} \]
其中,平方和與立方和爲
\[ \begin{aligned} \sum_{i=1}^n i^2 &= \frac{n(n+1)(2n+1)}{6}\\ \sum_{i=1}^n i^3 &= \left(\frac{n(n+1)}{2}\right)^2 \end{aligned} \]
直接枚舉x後代入計算便可。
在計算\(x \times y \mod p\)時,因爲p的最大值爲\(10^{11}\),可能會超過long long的表示範圍,能夠將x拆成\((a\cdot 2^{20} + b)\),將y拆成\((c\cdot 2^{20} + d)\),將每次乘法的運算數範圍限制在\(2^{20}\)之內,能夠直接用((((((a * c) << 20) + (a * d + b * c)) % mod) << 20) + b * d) % mod計算出結果。
時間複雜度\(O(\sqrt{N})\)
by Asm.Def
#include <bits/stdc++.h> using namespace std; const int maxn = 100005; typedef long long LL; int mu[maxn], P[maxn], pcnt; bool not_p[maxn]; LL N, mod; const LL lb = (1LL << 20) - 1; inline LL mult(LL x, LL y, LL mod) { LL a = x >> 20, b = x & lb; LL c = y >> 20, d = y & lb; return ( ( ( ( ( (a * c) << 20) + (a * d + b * c) ) % mod) << 20) + b * d) % mod; } inline LL S2(LL N) { return mult(mult(N, N+1, 6*mod), (2*N+1), 6*mod) / 6; //return (__int128(N) * (N+1) * (2*N+1) / 6) % mod; } inline LL S3(LL N) { LL t = mult(N, N+1, mod<<1) >> 1; //LL t = (__int128(N) * (N+1) / 2) % mod; return mult(t, t, mod); } void init() { mu[1] = 1; for(int i = 2;i < maxn;++i) { if(!not_p[i]) P[pcnt++] = i, mu[i] = -1; for(int j = 0;j < pcnt;++j) { if(i * P[j] >= maxn) break; not_p[i * P[j]] = true; if(i % P[j] == 0) { mu[i * P[j]] = 0; break; } mu[i * P[j]] = -mu[i]; } } } void work() { LL ans = 0; for(int i = 1;LL(i) * i <= N;++i) if(mu[i]) { LL t = LL(i) * i, t2 = mult(t, t, mod); ans = (ans + mu[i] * mult( mult(t2,N+1,mod), S2(N/t), mod) ) % mod; ans = (ans - mu[i] * mult(mult(t2,t,mod), S3(N/t), mod)) % mod; } printf("%lld\n", (ans + mod) % mod); } int main() { init(); while(~scanf("%lld%lld", &N, &mod)) { work(); } return 0; }
by XLC
指望難度:Easy
K短路模板題。因爲數據均爲隨機生成,直接預處理出每一個點到終點的最短路後A*搜索便可。
by Haizs
指望難度:Medium
二分答案,那麼每次check至關因而給定一個半徑的圓,而後問這個圓最多覆蓋多少個點,咱們能夠枚舉一個點,而後再枚舉每一個與他距離<=2r的點,就能夠求出全部的相交弧,離散化以後,求出覆蓋最屢次的弧,就是答案了。複雜度\(O(n^2\log(n)\cdot \log(30000))\)。
#include <bits/stdc++.h> using namespace std; const int maxn = 605; const double eps = 1e-6; int i, j, t, n, m, k, z, y, x; double l, r, mid; int R; int cmp(double x) { if (fabs(x) < eps) return 0; if (x > 0) return 1; return -1; } struct point { double x, y; point() {} point(double _x, double _y) : x(_x), y(_y) {} void input() { scanf("%lf%lf", &x, &y); } friend point operator+(const point &a, const point &b) { return point(a.x + b.x, a.y + b.y); } friend point operator-(const point &a, const point &b) { return point(a.x - b.x, a.y - b.y); } double norm() { return sqrt(x * x + y * y); } double angl() { return atan2(y, x); } } poi[maxn]; double dis[maxn][maxn], ang[maxn][maxn]; pair<double, int> pdi[maxn]; #define mp(a, b) make_pair(a, b) bool check(double r) { int i, j, t, ans = 1, k; double d; for (i = 1; i <= n; i++) { t = 0; for (j = 1; j <= n; j++) if (i != j) { if (cmp(dis[i][j] - 2.0 * r) > 0) continue; d = acos(dis[i][j] / (2.0 * r)); pdi[++t] = mp(ang[i][j] - d, 1); pdi[++t] = mp(ang[i][j] + d, -1); } sort(pdi + 1, pdi + t + 1); k = 1; for (j = 1; j <= t; j++) { k += pdi[j].second; ans = max(ans, k); } } return ans >= m; } int main() { // cout << time(NULL) << endl; // freopen("tcial.in", "r", stdin); // freopen("tcial.out", "w", stdout); int T, I; scanf("%d", &T); for (I = 1; I <= T; I++) { scanf("%d%d", &n, &m); for (i = 1; i <= n; i++) poi[i].input(); scanf("%d", &R); if (m > n) { printf("The cake is a lie.\n"); continue; } for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) dis[i][j] = (poi[j] - poi[i]).norm(), ang[i][j] = (poi[j] - poi[i]).angl(); l = R; r = 30000; while (r - l > eps) { mid = (l + r) / 2; if (check(mid - R)) r = mid; else l = mid; } printf("%.6f\n", r); } // cout << time(NULL) << endl; return 0; }
https://nanti.jisuanke.com/t/31446](https://nanti.jisuanke.com/t/31446)
by Infi
指望難度:Easy
添加源點s,匯點t。
對於原圖的邊,定義流量爲[0,1],s對於N個點都連邊,流量爲[L,R],M個點對t都連邊,流量爲[L,R]。那麼就變成了有源匯上下界可行流問題。根據相關方法建圖便可。
by ZGH
指望難度:Easy+
觀察遞推方程,不難看出通項公式的形式:
\[ a_n = k p^n + an^2 + bn + c \]
代入後解得\(p=1, a=1, b=1, c=-k\)
即\(a_n = n^2 + n\)
則答案爲
\[ \begin{aligned} &\sum_{i=1}^{n} [\gcd(i, m)=1] (i^2 + i)\\ =&\sum_{d|m \land d \leq n} \mu(d) \cdot \sum_{t=1}^{[\frac{n}{d}]} ((td)^2 + td)\\ =&\sum_{d|m \land d \leq n} \mu(d) \cdot \left( d^2 \cdot \sum_{t=1}^{[\frac{n}{d}]} t^2 + d\cdot \sum_{t=1}^{[\frac{n}{d}]} t \right) \end{aligned} \]
對m分解質因數後dfs枚舉全部知足條件且\(\mu(d)\)不爲0的d,而後用求和公式計算後半部分的貢獻。
by Asm.Def
指望難度:Hard
將N表示爲
\[ N = \sum_{i=0}^{n-1} A_i 2^i,\ A_i \in \{0,1\} \]
因爲位與運算每一位是獨立的,不妨對每一位單獨考慮它對答案的貢獻:
\[ Ans(N) = \sum_{i=0}^{n-1}\left[A_i \cdot (1 +\sum_{j=0}^{i-1}A_j\cdot 2^j )+ 2^i \cdot \sum_{j=i+1}^{n-1} A_j\cdot 2^{j-i-1} \right]^2 \]
若是直接用FFT計算每次平方,時間複雜度爲\(O(n^2 \log n)\) ,難以接受。
考慮在N的某個區間\([L, R)\)上定義答案,並將答案寫成多項式的形式,即
\[ Ans_{[L,R)}(x)=\sum_{i=L}^{R-1} \left[A_i \cdot (1 +\sum_{j=L}^{i-1}A_j\cdot x^{j-L} )+ x^{i-L} \cdot \sum_{j=i+1}^{R-1} A_j\cdot x^{j-i-1} \right]^2 \]
其中有一項常數項,不方便合併,所以先將答案拆開:
\[ \begin{aligned} &Ans_{[L,R)}(x)\\ =&\sum_{i=L}^{R-1} \left[A_i^2 + 2A_i(A_i \cdot \sum_{j=L}^{i-1}A_j\cdot x^{j-L} + x^{i-L} \cdot \sum_{j=i+1}^{R-1} A_j\cdot x^{j-i-1}) \\+ (A_i \cdot \sum_{j=L}^{i-1}A_j\cdot x^{j-L} + x^{i-L} \cdot \sum_{j=i+1}^{R-1} A_j\cdot x^{j-i-1})^2 \right] \end{aligned} \]
考慮到\(A_i\)的取值範圍爲0或1,即\(A_i^2 = A_i\),可將答案轉化爲
\[ \begin{aligned} &Ans_{[L,R)}(x)\\ =&\sum_{i=L}^{R-1} A_i + 2 \sum_{i=L}^{R-1} A_i \cdot \left( \sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} \right) + \sum_{i=L}^{R-1} \left(A_i\sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} \right)^2 \end{aligned} \]
令
\[ \begin{array}{ccl} &S(i)&=&\sum\limits_{j=i}^{n-1} A_i\\ &F_{[L,R)}(x)&=&\sum\limits_{i=L}^{R-1}A_i x^i\\ &Ans1_{[L,R)}(x)&=&\sum\limits_{i=L}^{R-1} A_i \cdot \left( \sum\limits_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum\limits_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} \right)\\ &Ans2_{[L,R)}(x)&= &\sum\limits_{i=L}^{R-1} \left(A_i\sum\limits_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum\limits_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} \right)^2 \end{array} \]
則\(Ans_{[L,R)}(x) = S(L)-S(R) + 2Ans1_{[L,R)}(x) + Ans2_{[L,R)}(x)\)。
考慮如何合併兩個相鄰區間\([L, mid)\)、\([mid,R)\)的答案。
\[ \begin{aligned} Ans1_{[L,R)}(x)=&\sum_{i=L}^{R-1} A_i \cdot \left( \sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} \right)\\ =&\sum_{i=L}^{mid-1} A_i \cdot \left( \sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{mid-1} A_j\cdot x^{j-L-1} + \sum_{j=mid}^{R-1} A_j \cdot x^{j-L-1} \right)\\ &+ \sum_{i=mid}^{R-1} A_i \cdot \left( \sum_{j=mid}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} + \sum_{j=L}^{mid-1} x^{j-L} \right)\\ =&Ans1_{[L,mid)}(x) + Ans1_{[mid,R)}(x)\cdot x^{mid-L}\\ &+ \left(\sum_{i=L}^{mid-1} A_i \right) \cdot \left( \sum_{j=mid}^{R-1} A_j \cdot x^{j-L-1} \right)\cdot + \left(\sum_{i=mid}^{R-1} A_i \right) \cdot \left( \sum_{j=L}^{mid-1} A_j \cdot x^{j-L} \right)\\ =&Ans1_{[L,mid)}(x) + Ans1_{[mid,R)}(x)\cdot x^{mid-L} \\&+\sum_{j=mid}^{R-1}(S(L)-S(mid))\cdot A_j x^{j-L-1}+\sum_{j=L}^{mid-1}(S(mid)-S(R))\cdot A_j x^{j-L} \end{aligned} \]
\[ \begin{aligned} Ans2_{[L,R)}(x)=&\sum_{i=L}^{R-1} \left(A_i\sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} \right)^2\\ =&\sum_{i=L}^{mid-1} \left(A_i\sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{mid-1} A_j\cdot x^{j-L-1} + \sum_{j=mid}^{R-1}A_j\cdot x^{j-L-1} \right)^2 \\&+ \sum_{i=mid}^{R-1} \left(A_i\sum_{j=mid}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{R-1} A_j\cdot x^{j-L-1} + A_i\sum_{j=L}^{mid-1} A_j\cdot x^{j-L} \right)^2\\ =&Ans2_{[L,mid)}(x) + Ans2_{[mid, R)}(x) \cdot x^{2(mid-L)}\\ &+2\sum_{i=L}^{mid-1} \left(A_i\sum_{j=L}^{i-1}A_j\cdot x^{j-L} + \sum_{j=i+1}^{mid-1}A_j\cdot x^{j-L-1} \right)\cdot F_{[mid,R)}(x) x^{mid-L-1} \\ &+2\sum_{i=mid}^{R-1} A_i \left( \sum_{j=mid}^{i-1}A_j\cdot x^{j-mid} + \sum_{j=i+1}^{R-1}A_j\cdot x^{j-mid-1} \right) \cdot x^{mid-L} \cdot F_{[L,mid)}(x)\\ &+F_{[mid,R)}(x)^2\cdot (mid-L) \cdot x^{2(mid-L-1)}+F_{[L,mid)}(x)^2\cdot [S(mid) - S(R)] \end{aligned} \]
化簡到這裏,就能夠經過兩次長度爲\((mid-L)\)和\((R-mid)\)的FFT運算和若干次多項式加法、數乘和移位,由\([L,mid)\)和\([mid,R)\)在\(O((R-L)\log(R-L))\)的時間內求出\(Ans1_{[L,R)}(x), Ans2_{[L,R)}(x)\)。因爲待求的至關於x=2時的點值,因此每次用FFT進行乘法後均可以對結果進行一次進位,從而確保在相乘的過程當中係數不會溢出。
對\(Ans_{[0,n)}(2)\)分治求解,總複雜度\(O(n\log^2 n)\).
//Asm.Def #include <bits/stdc++.h> using namespace std; const int maxn = 200005, mod = 998244353, g = 3, maxk = 1 << 19; typedef long long LL; typedef vector<int> Poly; int A[maxn], N, Sum[maxn]; void Print(const Poly &ans) { for(int i = ans.size()-1;i >= 0;--i) printf("%d ", ans[i]); puts(""); } int powmod(int a, int n) { int ans = 1; while(n) { if(n & 1) ans = (LL) ans * a % mod; a = (LL) a * a % mod; n >>= 1; } return ans; } void Carry(Poly &x) { int C = 0, c; while(x.size() > 1) { if(x.back() == 0) x.pop_back(); else break; } for(int i = 0;i < x.size() || C;++i) { if(i < x.size()) { x[i] += C; C = x[i] >> 1; x[i] = x[i] & 1; } else { x.push_back(C & 1); C >>= 1; } } } //Poly operator * (const Poly &a, const Poly &b) //{ // Poly ans(a.size() + b.size() - 1); // for(int i = 0;i < (int) a.size();++i) // for(int j = 0;j < (int) b.size();++j) // ans[i+j] += a[i] * b[j]; // Carry(ans); // return ans; //} void Shuff(int A[], int n)//n爲位數 { static int B[maxk]; int N = 1 << n, j, mx = 1 << (n-1); for(int i = 0, it = 0;i < N;++i) { B[i] = A[it]; j = mx; while(it & j) { it ^= j; j >>= 1; } it ^= j; } for(int i = 0;i < N;++i) A[i] = B[i]; } void DFT(Poly &A, int n, int a)//n爲位數 { static int B[maxk], pw[20]; int N = 1 << n; pw[n-1] = a; for(int i = n-1;i;--i) pw[i-1] = (LL) pw[i] * pw[i] % mod; A.resize(N); for(int i = 0;i < N;++i) B[i] = A[i]; Shuff(B, n); for(int i = 0;i < n;++i) { int d = (1 << i), x0 = pw[i]; for(int j = 0;j < N;j += (d<<1)) { for(int k = j, x = 1;k < j+d;++k) { int t = (LL) x * B[k+d] % mod; B[k+d] = (B[k] + mod - t) % mod; B[k] = (B[k] + t) % mod; x = (LL) x * x0 % mod; } } } for(int i = 0;i < N;++i) A[i] = B[i]; //Print(A); } Poly operator * (const Poly &x, const Poly &y) { static Poly A, B; A = x, B = y; while(A.size() > 1) { if(A.back() == 0) A.pop_back(); else break; } while(B.size() > 1) { if(B.back() == 0) B.pop_back(); else break; } int n = 0; while((1<<n) < A.size() + B.size()) ++n; int a = powmod(g, (mod-1) >> n); DFT(A, n, a); DFT(B, n, a); for(int i = 0;i < (1 << n);++i) A[i] = (LL) A[i] * B[i] % mod; DFT(A, n, powmod(a, mod-2)); int t = powmod((1 << n), mod-2); for(int i = 0;i < (1 << n);++i) A[i] = (LL) A[i] * t % mod; //Print(A); Carry(A); return A; } Poly operator * (const Poly &a, int x) { Poly ans(a.size()); for(int i = 0;i < (int) a.size();++i) ans[i] = a[i] * x; Carry(ans); return ans; } Poly operator + (const Poly &a, const Poly &b) { Poly ans(max(a.size(), b.size())); for(int i = 0;i < (int) ans.size();++i) { ans[i] = 0; if(i < (int) a.size()) ans[i] += a[i]; if(i < (int) b.size()) ans[i] += b[i]; } return ans; } Poly operator << (const Poly &x, int n) { Poly ans(x.size() + n, 0); for(int i = 0;i < x.size();++i) ans[n+i] = x[i]; return ans; } //void Multi(const Poly &a, const Poly &b, Poly &ans) //{ // ans.resize(a.size() + b.size() - 1); // for(int i = 0;i < (int) a.size();++i) // for(int j = 0;j < (int) b.size();++j) // ans[i+j] += a[i] * b[j]; //} void init() { for(int i = 1;i <= N;++i) scanf("%1d", &A[N-i]); Sum[N] = 0; for(int i = N-1;i >= 0;--i) Sum[i] = Sum[i+1] + A[i]; } void Solve(int L, int R, Poly &Ans1, Poly &Ans2) { static Poly SL, SR, AL, AR; if(R - L == 1) { Ans1.resize(1);Ans1[0] = 0; Ans2.resize(1);Ans2[0] = 0; return; } int mid = (L + R) >> 1; Poly Ans1L, Ans1R, Ans2L, Ans2R; Solve(L, mid, Ans1L, Ans2L); Solve(mid, R, Ans1R, Ans2R); //printf("Solve (%d,%d)\n", L, R); // //Print(Ans1L); //Print(Ans1R); //Print(Ans2L); //Print(Ans2R); //puts(""); AL.clear(); AL.resize(mid-L); for(int i = L;i < mid;++i) AL[i-L] = A[i]; AR.clear(); AR.resize(R-mid); for(int i = mid;i < R;++i) AR[i-mid] = A[i]; SL.clear(); SL.resize(mid-L); for(int i = L;i < mid-1;++i) SL[i-L] = A[i] * (Sum[i+1]-Sum[mid]) + A[i+1] * (i+1-L); SR.clear(); SR.resize(R-mid); for(int i = mid;i < R-1;++i) SR[i-mid] = A[i] * (Sum[i+1]-Sum[R]) + A[i+1] * (Sum[mid]-Sum[i+1]); //puts(""); //Print(AL); //Print(AR); //Print(SL); //Print(SR); Ans2 = Ans2L + ((AR * SL) << (mid-L)) + ((AR * AR * (mid - L)) << (2 * (mid-L-1))) + (Ans2R << (2 * (mid-L))) + ((AL * SR) << (mid-L+1)) + AL * AL * (Sum[mid]-Sum[R]); Carry(Ans2); Ans1 = Ans1L + (Ans1R << (mid-L)); if((int) Ans1.size() < (R-L-1)) Ans1.resize(R-L-1); for(int i = L;i < mid;++i) Ans1[i-L] += A[i] * (Sum[mid] - Sum[R]); for(int i = mid;i < R;++i) Ans1[i-L-1] += A[i] * (Sum[L] - Sum[mid]); Carry(Ans1); //Print(Ans1); //Print(Ans2); } void work() { Poly ans1, ans2; Solve(0, N, ans1, ans2); //Print(ans1), Print(ans2); ans2 = ans2 + (ans1 << 1); ans2[0] += (Sum[0] - Sum[N]); Carry(ans2); int sum = 0; for(int i = ans2.size()-1;i >= 0;--i) printf("%d", ans2[i]); puts(""); //for(int i = 0;i < (int) ans.size();++i) // sum += ans[i]; //printf("%d\n", sum % 1000000007); } int main() { time_t beg = clock(); while(~scanf("%d", &N)) { init(); work(); } //printf("%.2f sec", double(clock() - beg) / CLOCKS_PER_SEC); return 0; }
by Joker
指望難度:Easy
簽到題。直接根據題意模擬便可,能夠採用map來減小編碼難度。
指望難度:Medium-
按每一層的結點個數分類討論,設閾值爲\(T\)。
當第\(L\)層的結點個數\(Size_L <T\)時,每次\(1\ L\ X\)操做只需枚舉這一層的全部結點,維護它們對每一個結點的答案產生的貢獻便可。
當第\(L\)層的結點個數\(Size_L >= T\)時,這樣的層不超過\(\frac{N}{T}\)個,對於操做1能夠直接對每一個這樣的層維護增長了多少point,對於每次詢問直接枚舉一遍便可。
對於第一種狀況,能夠用樹狀數組維護dfs序列上的區間和,時間複雜度\(O(Q(T\cdot \log N))\);
對於第二種狀況,時間複雜度\(O(Q\cdot \frac{N}{T})\)
則總時間複雜度爲\(O(Q \cdot (T \log N + \frac{N}{T}))\),取\(T=\sqrt{\frac{N}{\log N}}\) 最優。
時間複雜度\(O(Q\cdot\sqrt{N\log N})\) 。
by bird_14
by morejarphone
指望難度:Easy-
考慮到答案中任意一位都必須是1或質數,可知答案只可能由一、二、三、五、7構成。因爲任意兩個不爲1的數字構成的兩位數必定能夠被11整除,因此答案中除1外的數字只能出現一次;1最多出現2次,由於111能夠被3整除;而二、五、7三者必定不會有二者同時出現。所以知足條件的整數不會超過四位,所有預處理出來便可。