【20191025】考试

本文探讨了约瑟夫问题的经典算法,并提出了一种优化方案,通过记录特定状态值来减少计算复杂度,同时介绍了使用树状数组进行区间查询的技巧,以及最长上升公共子序列问题的动态规划解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

T1 嘟嘟噜

在这里插入图片描述
在这里插入图片描述
经典的约瑟夫问题。虽然这个题暴力一分不给
不难发现第一个被处决的人的编号是m mod n−1m\ mod \ n-1m mod n1。那么,如果我们把下一个人的编号看做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...mmodn201...n2
不难发现,一个人在这个子情景中的编号,将它+m+m+mmod 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(n1,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}\rfloornmn
考虑如何计算“一个子情景的编号在本情景下的编号”。
记子情景中有n′=n−⌊nm⌋n'=n-\lfloor\frac{n}{m}\rfloorn=nmn个人。那么:
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)=m1((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=1nj=i+1n(xi2yj2+xj2yi22xiyixjyj)
又可以化为:
(∑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=1nxi)(i=1nyi)(i=1nxiyi)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[i1][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[i1][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("");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值