其他分享
首页 > 其他分享> > PASSWORD系列赛题解

PASSWORD系列赛题解

作者:互联网

T1.PASSWORD0--Graph

时间限制: 100ms
题目背景

此题为PASSWORD1,2的前奏。。。

题目描述

小Z十分的生气!他急于前往小L家,可在他眼前出现了由n个城市,m条道路组成的图,每条路通过都需要一定时间和一定费用,小Z想走最小路径到小L家。但眼前的景象却难倒了兢兢业业学习计算机的他,他需要你的求助。

最小路径的定义如下:定义每条小Z所在城市到小L家所在城市的路径总时间是各条道路旅行时间的和,总费用是各条道路所支付费用的总和。一条路径越快,或者费用越低,该路径就越好。严格地说,如果一条路径比别的路径更快,而且不需要支付更多费用,它就比较好。反过来也如此理解。如果没有一条路径比某路径更好,则该路径被称为最小路径。

最小路径可能不止一条,也有可能不存在路径

现在,小Z想知道他有多少种方法到小L家。

输入格式:

第一行有四个整数,城市总数 n,道路总数 m,起点和终点城市 s,e;

接下来的 m 行每行描述了一条道路的信息,包括四个整数,两个端点 p,r,费用 c,以及时间 t;

两个城市之间可能有多条路径连接。

输出格式:

仅一个数,表示小Z的走法总数。

输入样例:

4 5 1 4
2 1 2 1
3 4 3 1
2 3 1 2
3 1 1 4
2 4 2 4

输出样例:

2

说明

对于全部数据,1≤n≤100,0≤m≤300,1≤s,e,p,r≤n,0≤c,t≤100,保证 s != e,p != r。

标签:
最短路,动态规划,树状数组,线段树

若边只有一个权值,很容易想到用最短路求解
但此题中,边有两个权值,于是需要额外添加一维状态(SPFA中dis数组)
结合题目中问路径条数,可想到dp
f(i,j)表示在i点且费用为j时的最少时间
(k,i)为k -> i有向边,Cost为费用,Tim为时间
P(i)为存在(k,i)边的点集
$$
f[i][j] = Min{f[k][j - cost(k,i)] + Tim(k,i)}(k \in P(i))
$$
期望时间复杂度:
$$
O(K * N^2 * V)(K为迭代常数)
$$

但这样,还是无法通过此题(100ms),所以要尝试对这个算法进行优化
在求解过程中,考虑新状态f(i,j),若已存在f(i,k)满足k<j且f(i,k) < f(i,j)
显然,f(i,j)不是最优解

于是可以进行剪枝:
对于每一个新状态f(i,j),我们查询f(i,0~j)的最小值MinT,仅当MinT > f(i,j)时才更新,在实现时,可用树状数组维护每一个f(i),这样增加了查询速度

期望时间复杂度:
$$
O(K * N^2 * V * Log_2N)
$$
它的实际效率比未优化前快得多

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1e4 + 5;
struct E
{
    int next,to,t,w;
}e[605];
int id,f[605],n,m,s,t,ans,que[1000005][2],dis[105][N],vis[105][N],tree[105][N];
void add(int x,int y,int z,int w)
{
    id++;
    e[id].to = y;
    e[id].next = f[x];
    e[id].t = z;
    e[id].w = w;
    f[x] = id;
}
inline int lowbit(int x)
{
    return x & (-x); 
}
int GET_MIN(int x,int y)//查询dis[x][0....y]的最小值
{
    ++y;
    int Min = 1e7;
    while(y > 0)
    {
        Min = min(tree[x][y],Min);
        y -= lowbit(y);
    }
    return Min;
}
void update(int x,int y,int val)
{
    ++y;
    while(y <= 100 * n)
    {
        tree[x][y] = min(tree[x][y],val);
        y += lowbit(y);
    }
    return ;
}
void spfa()
{
    memset(dis,63,sizeof dis);
    memset(tree,63,sizeof tree);
    que[1][0] = s;
    que[1][1] = 0;
    dis[s][0] = 0;
    update(s,0,0);
    for(int q1 = 1,q2 = 1;q1 <= q2; ++q1)
    {
        int x = que[q1][0],f1 = que[q1][1];
        vis[x][f1] = 0;
        for(int i = f[x];i;i = e[i].next)
        {
            int f2 = f1 + e[i].w,y = e[i].to;
            if(GET_MIN(y,f2) > dis[x][f1] + e[i].t)
            {
                dis[y][f2] = dis[x][f1] + e[i].t;
                update(y,f2,dis[y][f2]);
                if(!vis[y][f2])
                {
                    vis[y][f2] = 1;
                    que[++q2][0] = y;
                    que[q2][1] = f2;
                }
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m >> s >> t;
    for(int i = 1;i <= m; ++i)
    {
        int x,y,w,z;
        cin >> x >> y >> w >> z;
        add(x,y,z,w);
        add(y,x,z,w);
    }
    spfa();
    int Min = dis[0][0];
    for(int i = 0;i <= n * 100; ++i)
        if(dis[t][i] < Min)
        {
            ans++;
            Min = dis[t][i];
        }
    cout << ans << endl;
    return 0;
}

T2.PASSWORD1--Maths

时间限制:

题目描述

蒟蒻小Z同学历经千辛万苦(见PASSWORD0),来到了小L同学家门口,却发现此门竟是一扇密码门,门上显示一个整数n,要求你将1~n的数排列,使得所有数都不在原位上,密码为这样排列的方案数,由于小L为小Z考虑,因此数太大,于是请将答案取模10007后再输入密码。小Z由于智商不够,他又找到了你,他急需你的帮助!

输入格式:

输入整数n

输出格式:

输出小L家密码门的密码

输入样例:

4

输出样例:

9

说明

对于10%的数据,1 <= n <= 100
对于100%的数据,1 <= n <= 1e7

标签:
排列组合,错位排序

此题可直接运用错位排序递推式
D(i)表示将1~n的数重新排列,使所有数都不在原位上,排列的方案数
$$
D(1) = 0\
D(2) = 1\
D(n) = (n - 1) * (D(n - 1) + D(n - 2))
$$

证明:

显然对于n=1、2时,有D1=0,D2=1。
当n≥3时,在n个不同元素中任取一个元素ai不排在与其编号相对应的i位,必排在剩下n-1个位置之一,所以ai有n-1种排法。
对ai每一种排法,如ai排在j位,对应j位的元素aj的排位共有两种情况:
第一种情况:aj恰好排在i位上,此时,ai排在j位,aj排在i位,元素ai,aj排位已定,还剩n-2个元素,它们的排位问题就转化为n-2个元素全错位排列数,应有Dn-2种;
第二种情况:aj不排在i位上,此时,ai仍排在j位,aj不排在i位,即此时aj有一个不能排的位置,也就是说,除了ai外,还有n-1个元素,每个元素均有一个不能排的位置,问题就可转化为n-1个元素得全错位排列,排列数为Dn-1,由乘法原理和加法原理可得:Dn=(n-1)(Dn-1+Dn-2)(n≥3)。

代码:

#include <iostream>

using namespace std;

const int MOD = 10007;
int n,D[10000005];

int main()
{
    cin >> n;
    D[1] = 0;
    D[2] = 1;
    for(int i = 3;i <= n; ++i)
    {
        D[i] = (i - 1) % MOD * (D[i - 1] + D[i - 2]);
        D[i] %= MOD;
    }
    cout << D[n] << endl;
    return 0;
}

T3.PASSWORD2--Find

时间限制: 200ms
题目描述

由于小Z频繁打开小L家的门,于是小L把旧密码门换成了全新的密码门。 门上显示n个整数数对(ai,bi),还有一整数m,让你选出m个数对,密码为选出的m对(∑ai)/(∑bi)的最大值。

小Z突然发现,密码要保留四位小数,他十分想进去,需要求助你,让你来帮他打开全新密码门。

输入格式:

输入两整数n,m,第2行到n+1行为数对(ai,bi)

输出格式:

输出小L家密码门的密码

输入样例:

3 2
1 2
2 3
3 4

输出样例:

0.7142

说明

对于10%的数据,1 <= m <= n <= 20
对于100%的数据,1 <= m <= n <= 5000,1 <= |ai|,bi <= 100
数据均为Python随机生成
答案保留四位小数!!!

标签:
二分答案,01分数规划

此题几乎为模板题
$$
(\Sigma a_i)/(\Sigma b_i) \geq k\
\Sigma a_i \geq k * \Sigma b_i\
\Sigma a_i - k * \Sigma b_i \geq 0
$$
以上为二分的判断条件
c(i) = a(i) - k * b(i)
对c(i)从大到小排序
c[1] + ... + c[m] = sum
sum >= 0即为可行,反之不可行
代码:

#include <iostream>
#include <algorithm>

using namespace std;

int n,m,a[19002],b[19002];
long double c[19002];
bool cmp(long double a,long double b)
{
    return a > b;
}
bool check(double g)
{
    for(int i = 1;i <= n;i++)
        c[i] = a[i] - g * b[i];
    sort(c + 1,c + n + 1,cmp);
    long double q = 0; 
    for(int i = 1;i <= m;i++)
        q += c[i];
    return q >= 0;
}

int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
        cin >> a[i] >> b[i];
     
    //[l,r)
    double l = -10001,r = 10001,mid;
    while(l + 0.0001 < r)
    {
        mid = (l + r) / 2;
        if(check(mid))
            l = mid;
        else
            r = mid;
    }
    printf("%.4lf",l);
    return 0;
}

T3.PASSWORD3--String

时间限制: 300ms
题目背景

hzy巨佬AK完NOIP后,于是想学(tui)习(fei),无聊中的他构思出一款游戏,把游戏规则告诉了小L、小Z和你,但需要计算机来快速地判断输赢,由于hzy巨佬不屑于开发这种如此简单的游戏,于是想让你开发出判断输赢的程序

题目描述

小Z终于走进了小L家的家门,由于十分疲惫,想跟小L玩hzy开发的游戏,游戏规则如下:

小L和小Z分别给出字符串A、B(只含大,小写字母)和整数C、D,由于他俩十分有默契,A包含B(保证A比B长)。

这时统计出B在A中出现的位置(位置从1开始计算,总是记B在A中每次开始的位置),组成了一个集合{pos}。

定义"分契合值"li=min(abs((int)A[posi]-(int)A[posj]))(1 <= j < i),特殊的,l1=A[pos1],"总契合值"S为∑li

若C离S较近,输出"L";

若D离S较近,输出"Z";

若C离S与D离S一样近,输出"AK NOIP!"(有叹号)

小Z十分贪玩,于是决定玩T局,现在请你写一个程序,让你给出每局的输赢情况

输入格式:

第一行一个整数T

之后4*T行,每四行中依次有:字符串A 字符串B 猜测数C 猜测数D

输出格式:

共T行,第i行表示第i局的输赢情况

输入样例:
3
AB
AB
1
1
AB
AB
1
5
AB
AB
5
1

输出样例:

AK NOIP!
L
Z

说明

B在A中出现次数<=100000,B.size()<=A.size()<=1e6,T<=20,C,D均在int范围内

标签:
平衡树,KMP

模板题,略
平衡树部分可见[HNOI2002]营业额统计

代码:

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>

using namespace std;

struct node
{
    int lc,rc,cnt,pos,vis;
    #define lc(x)t[x].lc
    #define rc(x)t[x].rc
    #define p(x)t[x].pos
    #define c(x)t[x].cnt
    #define v(x)t[x].vis 
}t[1000005];
char A[10000005],B[1000005];
int T,C,D,P[1000005],lenA,lenB,id,Ans,rt,pool;
void init()
{
    memset(P,0,sizeof P);
    id = 0;
}
void Zig(int &k)
{
    int y = lc(k);
    lc(k) = rc(y);
    rc(y) = k;
    k = y;
}
void Zag(int &k)
{
    int y = rc(k);
    rc(k) = lc(y);
    lc(y) = k;
    k = y;
}
void Insert(int &k,const int &key)
{
    if(!k)
    {
        k = ++pool;
        v(k) = key;
        p(k) = rand();
        c(k) = 1;
        lc(k) = rc(k) = 0;
        return ;
    }
    if(v(k) == key)
        ++c(k);
    else if(key < v(k))
    {
        Insert(lc(k),key);
        if(p(lc(k)) < p(k))
            Zig(k);
    }
    else
    {
        Insert(rc(k),key);
        if(p(rc(k)) < p(k))
            Zag(k);
    }
    return ;
}
int QueryPre(const int &key)
{
    int x = rt,res = -99999999;
    while(x)
    {
        if(v(x) <= key)
            res = v(x),x = rc(x);
        else
            x = lc(x);
    }
    return res;
}
int QuerySuf(const int &key)
{
    int x = rt,res = 99999999;
    while(x)
    {
        if(v(x) >= key)
            res = v(x),x = lc(x);
        else
            x = rc(x);
    }
    return res;
}
void pre()
{
    P[1] = 0;
    int j = 0;
    for(int i = 1;i < lenB; ++i)
    {
        while(j > 0 && B[j + 1] != B[i + 1])
            j = P[j];
        if(B[j + 1] == B[i + 1])
            j++;
        P[i + 1] = j; 
    }
}
void KMP()
{
    int j = 0,x,y;
    for(int i = 0;i < lenA; ++i)
    {
        while(j > 0 && A[i + 1] != B[j + 1])
            j = P[j];
        if(A[i + 1] == B[j + 1])
            j++;
        if(j == lenB)
        {
            if(id == 0)
                Insert(rt,Ans = (int)A[i - j + 2]);
            else
            {
                x = QueryPre((int)A[i - j + 2]),y = QuerySuf((int)A[i - j + 2]);
                Ans += min((int)A[i - j + 2] - x,y - (int)A[i - j + 2]);
                Insert(rt,(int)A[i - j + 2]);
            }
            j = P[j];
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> T;
    while(T--)
    {
        init();
        cin >> A + 1 >> B + 1 >> C >> D;
        lenA = strlen(A + 1);
        lenB = strlen(B + 1);
        pre();
        KMP();
        if(abs(C - Ans) < abs(D - Ans))
            cout << "L" << endl;
        else if(abs(C - Ans) > abs(D - Ans))
            cout << "Z" << endl;
        else
            cout << "AK NOIP!" << endl;
    }
    return 0;
}

T5.PASSWORD4--Maintain

题目背景
ZHT由于HZY的游戏而十分愤怒,于是他前往HZY家修改游戏源代码...
题目描述
ZHT来到HZY的电脑前,只见屏幕上显示:“回答如下问题,进入系统。”
这时,ZHT发现的HZY桌上的纸条:

问题如下:
有初始数列a1,a2,...,an,还有模数MOD
操作:
(1)把数列中的一段数全部乘一个值;
(2)把数列中的一段数全部加一个值;
(3)把数列中的一段数全部取模一个值;
问题:
   数列中的一段数的和,由于答案可能很大,你只需回答这个数模MOD的值。
   
擅长数学的你(ZHT),离开计算机独立试试吧!

----------------HZY

蒟蒻ZHT无能为力,只好求助拥有计算机的你

输入格式

第一行两个整数n和MOD(1≤MOD≤1e9)。n,m <= 1e5
第二行含有N个非负整数,从左到右依次为a1,a2,…,an, (0≤ai≤1e9,1≤i≤n)。
第三行有一个整数m,表示操作总数。
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c(1≤t≤g≤N,1≤c≤1e9)。
操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,1≤c≤1e9)。
操作3:“3 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai%c (1≤t≤g≤n,1≤c≤1e9)。
问题:“Q t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模MOD的值 (1≤t≤g≤n)。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式

对每个问题,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

样例输入

7 43
1 2 3 4 5 6 7
5
1 2 5 5
Q 2 4
2 3 7 9
Q 1 3
Q 4 7

样例输出

2
35
8

数据范围:
对于30%的数据,n,m ≤ 1e3
对于100%的数据,n,m ≤ 1e5


思路

线段树模板题
区间加法,区间乘法都很好做,只需维护懒标记add[],mul[]即可
:mul[]初始值为1)
区间取模:显然,区间取模无法用懒标记解决,但大量的单点修改速度会很慢,但注意到,只有被取模数大于等于模数,取模才有效,故想到额外维护区间最大值,若最大值小于模数,当前区间就不用考虑,这样就加快了许多

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 1e5 + 5;
typedef long long ll;
ll n,MOD,m,add[N << 2],mul[N << 2],sum[N << 2],Max[N << 2],a[N];
inline void Mul(int k,ll v)
{
    mul[k] *= v;
    add[k] *= v;
    sum[k] *= v;
    Max[k] *= v;
    Max[k] %= MOD;
    mul[k] %= MOD;
    add[k] %= MOD;
    sum[k] %= MOD; 
    return ;
}
inline void Add(int k,int l,int r,ll v)
{
    add[k] += v;
    sum[k] += (r - l + 1) * v;
    Max[k] += v;
    Max[k] %= v;
    sum[k] %= MOD;
    add[k] %= MOD;
    return ;
}
inline void PushUP(int k)
{
    sum[k] = (sum[k << 1] + sum[k << 1 | 1]) % MOD;
    Max[k] = (max(Max[k << 1],Max[k << 1 | 1])) % MOD;
    return ;
}
inline void build(int k,int l,int r)
{
    if(l == r)
    {
        sum[k] = Max[k] = a[l];
        return ;
    }
    int mid = l + r >> 1;
    build(k << 1,l,mid);
    build(k << 1 | 1,mid + 1,r);
    PushUP(k);
}
inline void PushdownMul(int k,int l,int r)
{
    if(mul[k] == 1)
        return ;
    Mul(k << 1,mul[k]);
    Mul(k << 1 | 1,mul[k]);
    mul[k] = 1;
}
inline void PushdownAdd(int k,int l,int r)
{
    if(!add[k])
        return ;
    int mid = l + r >> 1;
    Add(k << 1,l,mid,add[k]);
    Add(k << 1 | 1,mid + 1,r,add[k]);
    add[k] = 0;
}
inline void modify(int k,int l,int r,int L,int R,ll v,bool ch)
{
    if(L <= l && r <= R)
    {
        if(ch == 0)
            Mul(k,v);
        else
            Add(k,l,r,v);
        return ;
    }
    int mid = l + r >> 1;
    PushdownMul(k,l,r);
    PushdownAdd(k,l,r);
    if(L <= mid)
        modify(k << 1,l,mid,L,R,v,ch);
    if(mid < R)
        modify(k << 1 | 1,mid + 1,r,L,R,v,ch);
    PushUP(k);
}
inline void modifyMOD(int k,int l,int r,int L,int R,ll v)
{
    if(Max[k] < v)
        return ;
    if(l == r)
    {
        sum[k] %= v;
        Max[k] %= v;
        add[k] %= v;
        mul[k] %= v;
        return ;
    }
    int mid = l + r >> 1;
    PushdownMul(k,l,r);
    PushdownAdd(k,l,r);
    if(L <= mid)
        modifyMOD(k << 1,l,mid,L,R,v);
    if(mid < R)
        modifyMOD(k << 1 | 1,mid + 1,r,L,R,v);
    PushUP(k);
}
inline ll query(int k,int l,int r,int L,int R)
{
    if(L <= l && r <= R)
        return sum[k];
    int mid = l + r >> 1;
    PushdownMul(k,l,r);
    PushdownAdd(k,l,r);
    ll res = 0;
    if(L <= mid)
    {
        res += query(k << 1,l,mid,L,R);
        res %= MOD;
    }
    if(mid < R)
    {
        res += query(k << 1 | 1,mid + 1,r,L,R);
        res %= MOD;
    }
    return res;
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> MOD;
    for(int i = 1;i <= n << 2; ++i)
        mul[i] = 1;
    for(int i = 1;i <= n; ++i)
        cin >> a[i];
    build(1,1,n);
    cin >> m;
    ll c;
    int t,g;
    for(int i = 1;i <= m; ++i)
    {
        char op;
        cin >> op >> t >> g;
        if(op == '1')
        {
            cin >> c;
            modify(1,1,n,t,g,c,0);
        }
        else if(op == '2')
        {
            cin >> c;
            modify(1,1,n,t,g,c,1);
        }
        else if(op == '3')
        {
            cin >> c;
            modifyMOD(1,1,n,t,g,c);
        }
        else
            cout << query(1,1,n,t,g) << endl;
    }
}

标签:include,lc,int,题解,系列赛,++,ai,rc,PASSWORD
来源: https://www.cnblogs.com/pipa-peng/p/10386046.html