ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

PASSWORD系列赛题解

2019-02-15 21:40:02  阅读:292  来源: 互联网

标签:include lc int 题解 系列赛 ++ ai rc 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

时间限制:

  • 1~7评测点,100ms
  • 8~10评测点,200ms

题目描述

蒟蒻小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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有