T1 嘟嘟噜
经典的约瑟夫问题。虽然这个题暴力一分不给。
不难发现第一个被处决的人的编号是m mod n−1m\ mod \ n-1m mod n−1。那么,如果我们把下一个人的编号看做000,其他人的编号为前一个人+1+1+1,这个问题的规模就被缩小了。
我们画个表:
mmod n0mmod n+11......mmod n−2n−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再mod nmod\ nmod n后,即为当前情景中的编号。
我们记f(n,m)f(n,m)f(n,m)表示答案。则:
f(n,m)=(f(n−1,m)+m)mod nf(1,m)=0f(n,m)=(f(n-1,m)+m)\mod n \\ f(1,m)=0f(n,m)=(f(n−1,m)+m)modnf(1,m)=0
然而这个算法的复杂度为O(n)O(n)O(n),并不能满足本题的要求
发现当n>mn>mn>m时,一轮下来可能有很多人退出,而这些人的编号我们显然可以轻松地求出来(就是m,2m,⋯ ,km<nm,2m,\cdots,km<nm,2m,⋯,km<n)。
将剩下的人重编号后,原问题的规模从nnn缩小到n−⌊nm⌋n-\lfloor\frac{n}{m}\rfloorn−⌊mn⌋。
考虑如何计算“一个子情景的编号在本情景下的编号”。
记子情景中有n′=n−⌊nm⌋n'=n-\lfloor\frac{n}{m}\rfloorn′=n−⌊mn⌋个人。那么:
f(n,m)=((f(n′,m)−(nmod m))mod n′)×mm−1f(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=1n∑j=i+1n(xi2yj2+xj2yi2−2xiyixjyj)\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=1nxi)(∑i=1nyi)−(∑i=1nxiyi)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]为aaa选前iii个,bbb选前jjj个,且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[pos]<a[i]b[pos]<a[i]b[pos]<a[i]的f[i−1][pos]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("");
}