T1 嘟嘟噜
经典的约瑟夫问题。虽然这个题暴力一分不给。
不难发现第一个被处决的人的编号是
m
m
o
d
n
−
1
m\ mod \ n-1
m mod n−1。那么,如果我们把下一个人的编号看做
0
0
0,其他人的编号为前一个人
+
1
+1
+1,这个问题的规模就被缩小了。
我们画个表:
m
m
o
d
n
0
m
m
o
d
n
+
1
1
.
.
.
.
.
.
m
m
o
d
n
−
2
n
−
2
\begin{array}{c|c} m\mod n &0 \\\hline m\mod n +1&1 \\\hline ...&... \\\hline m\mod n - 2&n-2 \\ \end{array}
mmodnmmodn+1...mmodn−201...n−2
不难发现,一个人在这个子情景中的编号,将它
+
m
+m
+m再
m
o
d
n
mod\ n
mod n后,即为当前情景中的编号。
我们记
f
(
n
,
m
)
f(n,m)
f(n,m)表示答案。则:
f
(
n
,
m
)
=
(
f
(
n
−
1
,
m
)
+
m
)
m
o
d
n
f
(
1
,
m
)
=
0
f(n,m)=(f(n-1,m)+m)\mod n \\ f(1,m)=0
f(n,m)=(f(n−1,m)+m)modnf(1,m)=0
然而这个算法的复杂度为
O
(
n
)
O(n)
O(n),并不能满足本题的要求
发现当
n
>
m
n>m
n>m时,一轮下来可能有很多人退出,而这些人的编号我们显然可以轻松地求出来(就是
m
,
2
m
,
⋯
,
k
m
<
n
m,2m,\cdots,km<n
m,2m,⋯,km<n)。
将剩下的人重编号后,原问题的规模从
n
n
n缩小到
n
−
⌊
n
m
⌋
n-\lfloor\frac{n}{m}\rfloor
n−⌊mn⌋。
考虑如何计算“一个子情景的编号在本情景下的编号”。
记子情景中有
n
′
=
n
−
⌊
n
m
⌋
n'=n-\lfloor\frac{n}{m}\rfloor
n′=n−⌊mn⌋个人。那么:
f
(
n
,
m
)
=
(
(
f
(
n
′
,
m
)
−
(
n
m
o
d
m
)
)
m
o
d
n
′
)
×
m
m
−
1
f(n,m)=\frac{((f(n',m)-(n\mod m))\mod n')\times m}{m-1}
f(n,m)=m−1((f(n′,m)−(nmodm))modn′)×m
#include<bits/stdc++.h>
using namespace std;
inline int solve(int n, int m)
{
if(n == 1) return 0;
if(n >= m)
{
int tmp = n - (n / m);
return 1ll * m * ((solve(tmp, m) - n % m + tmp) % tmp) / (m - 1);
}
else
return (solve(n-1, m) + m) % n;
}
int main()
{
int T, n, m;
scanf("%d", &T);
while(T--)
scanf("%d%d", &n, &m), printf("%d\n", solve(n, m) + 1);
}
T2 天才绅士少女助手克里斯蒂娜
不要管那坨红的是什么
由叉积的计算式:
∑
i
=
1
n
∑
j
=
i
+
1
n
(
x
i
2
y
j
2
+
x
j
2
y
i
2
−
2
x
i
y
i
x
j
y
j
)
\sum_{i=1}^n\sum_{j=i+1}^n(x_i^2y_j^2+x_j^2y_i^2-2x_iy_ix_jy_j)
i=1∑nj=i+1∑n(xi2yj2+xj2yi2−2xiyixjyj)
又可以化为:
(
∑
i
=
1
n
x
i
)
(
∑
i
=
1
n
y
i
)
−
(
∑
i
=
1
n
x
i
y
i
)
2
(\sum_{i=1}^nx_i)(\sum_{i=1}^ny_i)-(\sum_{i=1}^nx_iy_i)^2
(i=1∑nxi)(i=1∑nyi)−(i=1∑nxiyi)2
用树状数组维护即可。注意常数qwq
#include<bits/stdc++.h>
#define ll int
using namespace std;
const int mn = 1000005, mod = 20170927;
int x[mn], y[mn], n;
ll c[3][mn];
inline int getint()
{
int ret = 0, flg = 1; char c;
while((c = getchar()) < '0' || c > '9')
if(c == '-') flg = -1;
while(c >= '0' && c <= '9')
ret = ret * 10 + c - '0', c = getchar();
return ret * flg;
}
inline ll MOD(ll x)
{
if(x < 0) x += (mod << 1);
if(x > mod) x -= mod;
return x;
}
inline void update(int pos, int p, ll v)
{
while(p <= n) c[pos][p] = MOD(c[pos][p] + v), p += p & -p;
}
inline ll getsum(int pos, int p)
{
ll ret = 0;
while(p) ret = MOD(ret + c[pos][p]), p -= p & -p;
return ret;
}
inline ll times(ll x) {return 1ll * x * x % mod;}
inline ll getres(int i, int l, int r)
{
ll ret = MOD(getsum(i, r) - getsum(i, l - 1));
return ret;
}
int main()
{
int m, T, l, r, p, a, b;
n = getint(), m = getint();
for(int i = 1; i <= n; i++)
x[i] = getint(), y[i] = getint(),
update(0, i, 1ll * x[i] * x[i] % mod), update(1, i, 1ll * y[i] * y[i] % mod), update(2, i, 1ll * x[i] * y[i] % mod);
while(m--)
{
T = getint() - 1;
if(T) l = getint(), r = getint(), printf("%d\n", MOD(1ll * getres(0, l, r) * getres(1, l, r) % mod - times(getres(2, l, r))));
else
{
p = getint(), a = getint(), b = getint(),
update(0, p, -1ll * x[p] * x[p] % mod), update(1, p, -1ll * y[p] * y[p] % mod), update(2, p, -1ll * x[p] * y[p] % mod),
x[p] = a, y[p] = b, update(0, p, 1ll * x[p] * x[p] % mod), update(1, p, 1ll * y[p] * y[p] % mod), update(2, p, 1ll * x[p] * y[p] % mod);
}
}
}
T3 凤凰院凶真
最长上升公共子序列裸题。
你们康康这个菜鸡,高二了连普及组知识都不会,他完了
令
f
[
i
]
[
j
]
f[i][j]
f[i][j]为
a
a
a选前
i
i
i个,
b
b
b选前
j
j
j个,且
b
[
j
]
b[j]
b[j]必选时的答案,则:
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
k
]
+
1
(
b
[
k
]
<
b
[
j
]
&
a
[
i
]
=
b
[
j
]
)
f[i][j]=f[i-1][k]+1(b[k]<b[j] \&\ a[i]=b[j])
f[i][j]=f[i−1][k]+1(b[k]<b[j]& a[i]=b[j])
优化时,只需记录满足
b
[
p
o
s
]
<
a
[
i
]
b[pos]<a[i]
b[pos]<a[i]的
f
[
i
−
1
]
[
p
o
s
]
f[i-1][pos]
f[i−1][pos]最大值即可。
#include<bits/stdc++.h>
using namespace std;
const int mn = 5005;
int f[mn][mn], a[mn], b[mn], n, pos, pre[mn][mn], ans[mn], cnt;
void print(int i, int j)
{
if(!i || !j) return;
print(i - 1, pre[i][j]), printf("%d", b[j]), ++cnt, putchar(cnt == f[n][pos] ? '\n' : ' ');
}
int main()
{
int m;
scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
scanf("%d", &m); for(int i = 1; i <= m; i++) scanf("%d", &b[i]);
memset(pre, -1, sizeof pre);
for(int i = 1; i <= n; i++)
{
int val = 0, tmp = 0;
for(int j = 1; j <= m; j++)
{
f[i][j] = f[i-1][j];
if(b[j] < a[i] && val < f[i-1][j])
val = f[i-1][j], tmp = j;
if(a[i] == b[j]) f[i][j] = val + 1, pre[i][j] = tmp;
}
}
for(int i = 1; i <= m; i++)
if(f[n][pos] < f[n][i]) pos = i;
printf("%d\n", f[n][pos]);
if(!f[n][pos]) return 0;
for(int i = n; i; i--)
if(pre[i][pos] != -1) ans[++cnt] = b[pos], pos = pre[i][pos];
printf("%d", ans[cnt--]);
while(cnt)
printf(" %d", ans[cnt--]);
puts("");
}