【20191025】考试

T1 嘟嘟噜

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

余额充值