A. Gudako and Ritsuka

链接

by Yuki & Asm.Def

期望难度:Hard-

考虑从后往前进行博弈动态规划,在这一过程中维护所有的先手必胜区间。区间不妨采用左开右闭,方便转移。

考虑一次转移,如果当前Servant的后一个位置属于对手,则当前Servant的必胜区间可以通过将后一个Servant的每个必败区间的左端点+1、右端点+x得到;如果后一个位置属于自己,则可以通过将后一个Servant的必胜区间做同样的操作得到。不妨分别对必胜区间左右端点维护一个偏移量,需要从对手进行转移时只需修改偏移量后交换左右端点的集合,然后再在左端点的集合里插入一个0即可。

需要注意的是,这样得到的必胜区间会有重叠,可能导致对下一个对手的必胜区间的统计出错。考虑到每次转移时所有的同类区间的长度的变化量都相同,可以分别用两个优先队列维护这两类区间,每次转移后暴力合并重叠的区间即可。

复杂度\(O((A+B) \log(A+B))\)

//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;
}

B. Call of Accepted

链接

期望难度:Medium

表达式求值问题可以将中缀表达式转换为后缀表达式,其中转换步骤使用调度场算法后缀表达式的求值 均在维基百科中有详细的介绍。

根据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

C. Convex Hull

链接

期望难度:Medium

Solution 1

不考虑外层循环的情况,那么答案显然是:

\[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

Solution2

答案可转化为

\[\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;
}

D. Made In Heaven

链接

by XLC

期望难度:Easy

K短路模板题。由于数据均为随机生成,直接预处理出每个点到终点的最短路后A*搜索即可。

E. The Cake Is A Lie

链接

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;
}

F. Fantastic Graph

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]。那么就变成了有源汇上下界可行流问题。根据相关方法建图即可。

G. Spare Tire

链接

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,然后用求和公式计算后半部分的贡献。

H. Hamming Weight

链接

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;
}

I. Lattice's basics in digital electronics

链接

by Joker

期望难度:Easy

签到题。直接根据题意模拟即可,可以采用map来减少编码难度。

J. Ka Chang

链接

期望难度: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

K. Supreme Number

链接

by morejarphone

期望难度:Easy-

考虑到答案中任意一位都必须是1或质数,可知答案只可能由1、2、3、5、7构成。由于任意两个不为1的数字构成的两位数一定可以被11整除,所以答案中除1外的数字只能出现一次;1最多出现2次,因为111可以被3整除;而2、5、7三者一定不会有两者同时出现。因此满足条件的整数不会超过四位,全部预处理出来即可。

【ACM-ICPC 2018 沈阳赛区网络预赛】不太敢自称官方的出题人题解的更多相关文章

  1. ACM-ICPC 2018 沈阳赛区网络预赛 K Supreme Number(规律)

    https://nanti.jisuanke.com/t/31452 题意 给出一个n (2 ≤ N ≤ 10100 ),找到最接近且小于n的一个数,这个数需要满足每位上的数字构成的集合的每个非空子集 ...

  2. ACM-ICPC 2018 沈阳赛区网络预赛-K:Supreme Number

    Supreme Number A prime number (or a prime) is a natural number greater than 11 that cannot be formed ...

  3. ACM-ICPC 2018 沈阳赛区网络预赛-D:Made In Heaven(K短路+A*模板)

    Made In Heaven One day in the jail, F·F invites Jolyne Kujo (JOJO in brief) to play tennis with her. ...

  4. 图上两点之间的第k最短路径的长度 ACM-ICPC 2018 沈阳赛区网络预赛 D. Made In Heaven

    131072K   One day in the jail, F·F invites Jolyne Kujo (JOJO in brief) to play tennis with her. Howe ...

  5. ACM-ICPC 2018 沈阳赛区网络预赛 J树分块

    J. Ka Chang Given a rooted tree ( the root is node 11 ) of NN nodes. Initially, each node has zero p ...

  6. ACM-ICPC 2018 沈阳赛区网络预赛 K. Supreme Number

    A prime number (or a prime) is a natural number greater than 11 that cannot be formed by multiplying ...

  7. ACM-ICPC 2018 沈阳赛区网络预赛 F. Fantastic Graph

    "Oh, There is a bipartite graph.""Make it Fantastic." X wants to check whether a ...

  8. Fantastic Graph 2018 沈阳赛区网络预赛 F题

    题意: 二分图 有k条边,我们去选择其中的几条 每选中一条那么此条边的u 和 v的度数就+1,最后使得所有点的度数都在[l, r]这个区间内 , 这就相当于 边流入1,流出1,最后使流量平衡 解析: ...

  9. ACM-ICPC 2018 沈阳赛区网络预赛 F Fantastic Graph(贪心或有源汇上下界网络流)

    https://nanti.jisuanke.com/t/31447 题意 一个二分图,左边N个点,右边M个点,中间K条边,问你是否可以删掉边使得所有点的度数在[L,R]之间 分析 最大流不太会.. ...

随机推荐

  1. 【codeforces】【比赛题解】#931 CF Round #468 (Div. 2)

    因为太迟了,所以没去打. 后面打了Virtual Contest,没想到拿了个rank 3,如果E题更快还能再高,也是没什么想法. [A]Friends Meeting 题意: 在数轴上有两个整点\( ...

  2. scp加端口号

    scp -P 21110 root@192.168.0.1:/home/abc.txt root@192.168.0.2:/root 注意: 参数-P 的位置一定要紧跟在scp命令后面 参数-P 指的 ...

  3. C++学习之路(十一):C++的初始化列表

    结论: 1.在C++中,成员变量的初始化顺序与变量在类型中的声明顺序相同,而与他们在构造函数的初始化列表中的顺序无关. 2.构造函数分为两个阶段执行:1)初始化阶段:2)普通的计算阶段,表现为赋值操作 ...

  4. MVVM设计模式的事件绑定

    为什么要事件绑定 这个问题其实是很好理解的,因为事件是丰富多样的,单纯的命令绑定远不能覆盖所有的事件.例如Button的命令绑定能够解决Click事件的需求,但Button的MouseEnter.窗体 ...

  5. c++ 类的构造顺序

    在单继承的情况下,父类构造先于子类,子类析构先于父类,例: class A { public: A() { cout << "A" << endl; } ~ ...

  6. 一、Vue入门

    vue官网:https://cn.vuejs.org/ 学习路线:VueJs2.0建议学习路线 在浏览器上安装 Vue Devtools工具 1.vue入门 <script src=" ...

  7. YUI Compressor 压缩 JavaScript 原理-《转载》

    YUI Compressor 压缩 JavaScript 的内容包括: 移除注释 移除额外的空格 细微优化 标识符替换(Identifier Replacement) YUI Compressor包括 ...

  8. MyBatis3-实现MyBatis分页

    此文章中的例子是沿用上一篇文章http://www.cnblogs.com/EasonJim/p/7055499.html的Spring MVC集成的例子改装的. MyBatis分页有以下方式实现: ...

  9. mysql数据库查找类型不匹配

    无意中看到10级学长的博客,提到了mysql数据库类型查找不匹配的问题,博客地址是:卢俊达 . 数据库中建表中会对每个属性进行类型划分,然后在查找数据库select时: MySQL 的文档 (Type ...

  10. 为什么有些网页QQ浏览器右键没有检查

    改成[极速内核]. 设置->高级设置 改成[极速内核]: 如下图: