Live2D

QBXT考前刷题九套整理集合(留坑,望一天能整理完啊)

Round1:P146 出题人:ZHX

T1

Description

众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。小葱同学
最近醉心于动态规划的研究,他苦学百年,已经牢牢掌握了最长上升子序列的知识。小葱对于这
种单调不减的序列非常着迷,于是他灵机一动,挥笔写下了一个数𝑥,现在小葱同学希望找到一个
最大的小于等于𝑥的数,使得这个数的各个数位是单调不减的,求这个数。
Input
一行一个数x
Output
一行一个数代表答案
题解:对于一个数若在某一位违反了单调不下降的性质即num[i-1]>num[i],显然要调整这两位的数字
因为要找到一个最大的小于等于x的数,所以显然要将num[i-1]减小1,num[i]及以后的数均改为9,
注意特判x=0,注意输出时特判前导0。
考场得分:90’ 错因:没有特判x=0。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 100010

using namespace std;

char s[maxn];
int num[maxn];
int pre[maxn];

int main()
{
    freopen("increase.in","r",stdin);
    freopen("increase.out","w",stdout);
    scanf("%s",s+1);
    int len=strlen(s+1);
    if(len==1&&s[1]=='0')
    {
        puts("0\n");
        return 0;
    }
    for(register int i=1;i<=len;++i)
        num[i]=s[i]-'0';
    pre[1]=1;
    for(register int i=2;i<=len;++i)
    {
        if(num[i]==num[i-1]) pre[i]=pre[i-1];
        else pre[i]=i;
    }
    bool flag=false;
    for(register int i=2;i<=len;++i)
    {
        if(num[i]<num[i-1])
        {
            int tmp=pre[i-1];
            --num[tmp];
            for(register int j=1;j<=tmp;++j)
            {
                if((!flag)&&(num[j]==0)) continue;
                else printf("%d",num[j]),flag=true;
            }
            for(register int j=tmp+1;j<=len;++j)
                printf("9");
            return 0;
        }
        if(i==len)
        {
            for(register int i=1;i<=len;++i)
                printf("%d",num[i]);
        }
    }
    return 0;    
} 
View Code

T2

Description

众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。小葱同学自幼学习乘法
和加法,并且小葱同学意识到,正是因为有了加法和乘法,才能够计算1 + 1 = 2和1 × 1 = 1这种高深的问题。
现在小葱给你𝑁个数𝑎1, 𝑎2, ⋯ , 𝑎𝑁,求下列式子的值:

 题解:归并排序+前缀和维护+快速乘

通过式子会发现满足相乘的ai和aj均为逆序对,然后通过找规律发现这两个逆序对在所有子串中出现的次数为

i*(n-j+1),通过归并排序找出逆序对,此时左区间的剩余所有数均与右集合待入辅助数组的数产生逆序对,此时

怎么办呢?循环??不太行,所以维护一下(ai*i)的前缀和即可,最后因为ai<=1e12直接乘会爆longlong,所以

要用快速乘。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 40010

using namespace std;

const long long mod=1e12+7;
long long n;
struct nn
{
    long long data;
    long long a;
    int pos;
};
nn num[maxn],f[maxn];
long long ans;
long long sum[maxn];

typedef long long ll;

inline ll mult_mod(ll a, ll b, ll m)
{
    ll res = 0;
    while(b){
        if(b&1) res = (res+a)%m;
        a = (a+a)%m;
        b >>= 1;
    }
    return res;
}

inline void merge_sort(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    merge_sort(l,mid); merge_sort(mid+1,r);
    int p1=l,p2=mid+1;
    for(register int i=l;i<=r;++i)
    {
        if(p1<=mid&&p2<=r)
        {
            if(num[p1].data<=num[p2].data) 
            {
               f[i].data=num[p1].data;
               f[i].pos=num[p1].pos;
               f[i].a=num[p1].a;  
               ++p1;
            }
            else 
            {
                long long tmp1=(sum[mid]-sum[p1-1])%mod;
                long long tmp2=(num[p2].data*(n-num[p2].pos+1))%mod;
                ans=(ans+mult_mod(tmp1,tmp2,mod))%mod;
                f[i].data=num[p2].data;
                f[i].pos=num[p2].pos;
                f[i].a=num[p2].a;
                ++p2;
            }
        }
        else if(p1<=mid) 
        {  
           f[i].data=num[p1].data;
           f[i].pos=num[p1].pos;
           f[i].a=num[p1].a;
           ++p1;
        }
        else if(p2<=r) f[i].data=num[p2].data,f[i].pos=num[p2].pos,f[i].a=num[p2].a,++p2;
    }
    for(register int i=l;i<=r;++i)
    {
        num[i].data=f[i].data;
        num[i].pos=f[i].pos;
        num[i].a=f[i].a;
        sum[i]=sum[i-1]+num[i].a;
    }
    return;
}

int main()
{
    freopen("multiplication.in","r",stdin);
    freopen("multiplication.out","w",stdout);
    scanf("%lld",&n);
    for(register int i=1;i<=n;++i)
    {
        scanf("%lld",&num[i].data);
        num[i].pos=i;
        num[i].a=num[i].pos*num[i].data;
        sum[i]=sum[i-1]+num[i].a;
    }
    merge_sort(1,n);
    printf("%lld",ans%mod);
    return 0;
}
View Code

T3

Description

小葱同学现在抵挡咕星人的进攻。咕星人的进攻非常猛烈,以至于小葱同学不得不进行防守。为了更好地防守咕星人的
进攻,小葱同学制作了𝑁面盾牌,其中第𝑖面盾牌的形状是左下角在(𝑎𝑖, 𝑏𝑖)右上角在(𝑐𝑖, 𝑑𝑖)的矩形。现在小葱将这𝑁面
盾牌组合在了一起,并且只有当这𝑁面盾牌组成了一个漂亮的矩形时,小葱才能够抵挡咕星人的进攻。一个漂亮的矩形指
的是盾牌不遗漏的覆盖了这个矩形内的每一个位置,并且每一个位置最多只被一个盾牌所覆盖(边界相交不算覆盖多次)。
现在给定你这𝑁面盾牌,你需要帮助小葱同学判断这𝑁面盾牌是否组成了一个漂亮的矩形。
题解:对于70分的做法,因为|x-y|<=200,所以可以染色。
      对于100分的做法,咕咕咕。

Round2 出题人:CZH
Round3 出题人:DZY
Round4 出题人:ZHX
round5 出题人:LJH
T1:
Description
小 h 有重度拖延症。
小 h 在学校有各式各样的课程,比如数学分析,高等代数等等。每门课有都有若干项作
业,每项作业都有一个必须完成时间,在这个时间之前必须完成。
小 h 不喜欢一天之中做多个作业,也不希望一天中什么作业也不做,所以小 h 每天会
完成一项作业。
同时,小 h 也不喜欢两个作业的必须完成时间在同一天,所以,他对这些作业的必须完
成时间做了一次重排,保证了每项作业的必须完成时间互不相同。
现在小 h 有 n 项作业,第 i 项作业的必须完成时间为 i。小 h 认为,一项作业在其必须
完成时间前后两天之内完成是合理的。(也就是说,若一项作业的必须完成时间为 t,那么这
项作业在 t-2, t-1, t, t+1, t+2 天完成都是符合小 h 要求的)。当然了,小 h 只能在第 1 天到
第 n 天完成这些作业。也就是第 1 项作业只能在第 1,第 2 或者第 3 天完成。
小 h 一下想出了好多种安排的方法,使得小 h 能够完美地完成各项作业。但是小 h 不
知道到底有多少种安排的方法。你能帮帮他吗?
题解:推出递推式+矩阵乘法加速
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

long long g[10][10];
long long ans[10][10];
long long dx[10][10];
const int p=1e9+7;
long long f[10];
long long res[10];

inline void ans_cf(int n)
{
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=n;j++)
            dx[i][j]=ans[i][j],ans[i][j]=0;
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=n;j++)
           for(register int k=1;k<=n;k++)
               ans[i][j]=(ans[i][j]+(g[i][k]*dx[k][j])%p)%p;
    return;
}

inline void x_cf(int n)
{
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=n;j++)
            dx[i][j]=g[i][j],g[i][j]=0;
    for(register int i=1;i<=n;i++)
        for(register int j=1;j<=n;j++)
            for(register int k=1;k<=n;k++)
               g[i][j]=(g[i][j]+(dx[i][k]*dx[k][j])%p)%p;
}

inline void fast_pow(long long n,long long w)
{
    while(w)
    {
        if(w%2==1) ans_cf(n);
        w/=2;
        x_cf(n);
    }
}

int main()
{
    freopen("delay.in","r",stdin);
    freopen("delay.out","w",stdout);
    long long n,k;
    scanf("%lld",&n);
    g[1][1]=1; g[1][2]=1; g[1][3]=1; g[1][4]=1; g[1][5]=2; g[1][6]=2;
    g[2][1]=1; g[2][2]=0; g[2][3]=0; g[2][4]=0; g[2][5]=0; g[2][6]=0;
    g[3][1]=0; g[3][2]=1; g[3][3]=0; g[3][4]=0; g[3][5]=0; g[3][6]=0;
    g[4][1]=0; g[4][2]=0; g[4][3]=1; g[4][4]=0; g[4][5]=0; g[4][6]=0;
    g[5][1]=0; g[5][2]=1; g[5][3]=0; g[5][4]=0; g[5][5]=1; g[5][6]=0;
    g[6][1]=0; g[6][2]=0; g[6][3]=0; g[6][4]=0; g[6][5]=0; g[6][6]=1;
    for(register int i=1;i<=6;++i)
        for(register int j=1;j<=6;++j)
            ans[i][j]=g[i][j];
    f[1]=1; f[2]=2; f[3]=6; f[4]=14; f[5]=31; f[6]=73;
    if(n<=6)
    {
        printf("%lld\n",f[n]);
        return 0;
    }
    f[1]=14; f[2]=6; f[3]=2; f[4]=1; f[5]=3; f[6]=1; 
    fast_pow(6,n-5);
    for(register int i=1;i<=6;++i)
        res[1]=(res[1]+f[i]*ans[1][i])%p;
    printf("%lld\n",res[1]%p);
    return 0;
}
View Code

T2

Description

小 h 准备规划一次旅行。
小 h 所处的国家是 C 国,他很喜欢旅行。最近他发现寒假将至,听说 J 国的风光很
好,便开始规划去 J 国旅行。J 国有 n 个城市,小 h 将这 n 个城市编号为 1--n。这个国
家,有些城市之间会有单向的公路相连,有些则不会。小 h 喜欢开车,因此,他希望他这
趟旅行是一个自驾游,自己开车游历国家。这个国家有 m 条公路,公路有长有短,汽车每
走一个单位的路程,就会花费一个单位的能源。小 h 并不富有,所以他规划中这次是一次
穷游,也就是花费尽可能少。而这趟自驾游中花费最多的就是汽车能源的费用了,所以他
希望花在补充能源上的费用尽可能少。
小 h 还是一个挑剔的人,他并不觉得每个城市他都值得去规划一趟旅行。他有 k 个心
目中值得旅行的城市,并且计划这趟自驾游是由一个值得旅行的城市去到另外一个值得旅
行的城市。他认为这样的自驾游很有意义,能够一次游历两个值得旅行的城市,并且能够
享受沿途风光。
由于小 h 是航空公司总裁的外甥的高中同学的大表弟的大学同学,因此他可以从 C 国
出发飞往 J 国的任何一个城市而不需要花钱,换言之,他可以从 J 国的任何一个国家出
发。方案很多,小 h 不知道哪个方案才是最省钱的方案,请问你能帮帮他吗?
题解:原题QwQ,二进制分组+最短路

对于每个被标记的点都跑一遍dij,然后在其它被标记的点中取min,复杂度 O(k*(n+m)logn)

对于暴力的优化:我们可以对这k个标记点进行二进制分组,分为两组,每次从set1向set2跑最短路取min,

再从set2向set1跑一遍最短路取min,因为若存在两个点之间的最短路为最小值,这两个点的下标的二进制

位一定有一位不一样,从而不被分在同一个组里,正确性被证明,这样只需要跑logk次即可。

复杂度 O(logk*(n+m)logn)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 100010
#define INF 2147483647

using namespace std;

struct node
{
    int ed,len,nxt;
};
node edge[maxn*5];
int n,m,k,first[maxn],cnt,dis[maxn];
int love[maxn];
int set1_cnt,set2_cnt;
int set1[maxn],set2[maxn];
bool vis[maxn];

inline void add_edge(int s,int e,int d)
{
    cnt++;
    edge[cnt].ed=e;
    edge[cnt].len=d;
    edge[cnt].nxt=first[s];
    first[s]=cnt;
    return;
}

inline void dijkstra()
{
    priority_queue < pair <int,int> > heap;
    for(register int i=1;i<=n;++i)
        dis[i]=INF,vis[i]=false;
    for(register int i=1;i<=set1_cnt;++i)
    {
        dis[set1[i]]=0;
        heap.push(make_pair(-dis[set1[i]],set1[i]));
    }
    while(!heap.empty())
    {
        int p=heap.top().second; heap.pop();
        if(vis[p]) continue;
        vis[p]=true;
        for(register int i=first[p];i;i=edge[i].nxt)
        {
            int e=edge[i].ed;
            int d=edge[i].len;
            int newd=dis[p]+d;
            if(newd<dis[e])
            {
                dis[e]=newd;
                heap.push(make_pair(-dis[e],e));
            }
        }    
    }
    return;
}

inline void dijkstra_2()
{
        priority_queue < pair <int,int> > heap;
    for(register int i=1;i<=n;++i)
        dis[i]=INF,vis[i]=false;
    for(register int i=1;i<=set2_cnt;++i)
    {
        dis[set2[i]]=0;
        heap.push(make_pair(-dis[set2[i]],set2[i]));
    }
    while(!heap.empty())
    {
        int p=heap.top().second; heap.pop();
        if(vis[p]) continue;
        vis[p]=true;
        for(register int i=first[p];i;i=edge[i].nxt)
        {
            int e=edge[i].ed;
            int d=edge[i].len;
            int newd=dis[p]+d;
            if(newd<dis[e])
            {
                dis[e]=newd;
                heap.push(make_pair(-dis[e],e));
            }
        }    
    }
    return;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(edge,0,sizeof(edge));
        memset(first,0,sizeof(first));
        cnt=0;
        scanf("%d%d%d",&n,&m,&k);
        for(register int i=1;i<=m;++i)
        {
            int s,e,d;
            scanf("%d%d%d",&s,&e,&d);
            add_edge(s,e,d);
        }
        int ans=2147483647;
        for(register int i=1;i<=k;++i)
            scanf("%d",&love[i]);
        for(register int i=0;(1<<i)<=k;++i)
        {
            set1_cnt=0;
            set2_cnt=0;
            for(register int j=1;j<=k;++j)
            {
                if(((j>>i)&1)==0) set1[++set1_cnt]=love[j];
                else set2[++set2_cnt]=love[j];
            }
            dijkstra();
            for(register int j=1;j<=set2_cnt;++j)
                ans=min(ans,dis[set2[j]]);
            dijkstra_2();
            for(register int j=1;j<=set1_cnt;++j)
                ans=min(ans,dis[set1[j]]);
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

T3

Description

有一棵 n 个点的树,每个点上有一个多重集。每次修改,会选择两个点 x 和 y,然后在
这两个点的最短路径上的的每个点(包括首尾两个点)的多重集都加入一个数 z。经过 m 次
操作后,树上每个点的权值定义为这个点上的多重集的众数。如果众数有多个,则取最小的
众数。
问:m 次操作后,每个点的权值分别为多少。

Round9:P150 出题人:ZHX
T1
Description
众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没
什么关系。
小葱同学为了庆祝题目套数突破150,小葱同学学习了凯撒加密算法。凯撒
加密算法是有一个字符串和一个数字加密串,我们不断书写数字加密串使之与字
符串对齐,然后对应位相加之后即可得到加密的结果。例如,如果给定的字符串
是 abcdz,而加密串是 123 的话,那么我们有:
abcdz
+12312
=bdfeb
现在给定字符串𝑠,数字串𝑛,问用𝑛对𝑠进行𝑘次加密操作之后得到的结果是多少。
题解:简单的模拟,只要对每个位进行加减操作即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 100010

using namespace std;

long long k;
int num[maxn];
char s_num[maxn],s[maxn];

int main()
{
    freopen("crazy.in","r",stdin);
    freopen("crazy.out","w",stdout); 
    scanf("%lld",&k);
    k=k%26;
    scanf("%s",s+1);
    scanf("%s",s_num+1);
    int len_s=strlen(s+1),len_num=strlen(s_num+1);
    for(register int i=1;i<=len_num;++i)
        num[i]=(int)(s_num[i]-'0');
    for(register int i=len_num+1;i<=len_s;++i)
    {
        if(i%len_num==0) num[i]=num[len_num];
        else num[i]=num[(i%len_num)];
    }
    char up='z';
    char down='a';
    for(register int i=1;i<=len_s;++i)
    {
        int tmp=(int)((num[i]*k)%26);
        if(tmp>((int)(up-s[i])))
        {
            tmp-=((int)up-s[i]);
            s[i]=(char)(((int)down)+tmp-1);
        }
        else s[i]=(char)(((int)s[i])+tmp);
        printf("%c",s[i]);
    }
    return 0;
} 
View Code

T2

小葱同学为了庆祝题目套数突破150,小葱同学自创了概率加密算法。现在
小葱同学有一个𝑁位的密码,密码的每一位都是1 − 𝑀中的一个数。现在小葱自
创的随机加密算法会给你𝑀个数𝑥𝑖,代表每次进行加密的时候,所有为𝑖的为都会
变成𝑥𝑖,而当𝑁位的密码全部变成和它一开始𝑁位一样之后,小葱同学的加密算
法也就停止了。所以,给定这个密码,然后求需要进行多少次加密是一个非常困
难的问题。为了简化这个问题,现在小葱假定我们并不知道输入的密码,输入的
密码是一个随机的密码,即总共𝑀^𝑁种可能性每种可能性的概率为𝑀^−𝑁。小葱同
学想要知道,在这种情况下,这个随机密码期望需要多少次能够完成加密操作。
题解:对于80分居然可以预处理每个数字变化多少次变回原来的数字,m^ndfs枚举
全排列找最小公倍数水过,正解是容斥原理

 

 
 
 

 

 

 
 
 
 
 
 

 

 

 

 

 

 

 

 

posted @ 2019-11-04 09:22  Hoyoak  阅读(520)  评论(0)    收藏  举报