QLU-第二次训练

本文解析了四道算法竞赛题目,包括海港统计、回文日期查找、求和问题及推销员路径规划,涵盖数据结构、模拟算法、数学推导及贪心策略。

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

A 海港

输出

输出n行,第i行输出一个整数表示第i艘船到达后的统计信息。

样例输入

3
1 4 4 1 2 2
2 2 2 3
10 1 3

样例输出

3
4
4

提示

【样例解释1】

第一艘船在第1秒到达海港,最近24小时到达的船是第一艘船,共有4个乘客, 分别是来自国家4,1,2,2共来自3个不同的国家;

第二艘船在第2秒到达海港,最近24小时到达的船是第一艘船和第二艘船,共有4 + 2 =6个乘客,分别是来自国家4,1,2,2,2,3共来自4个不同的国家;

第三艘船在第10秒到达海港,最近24小时到达的船是第一艘船、第二艘船和第 三艘船,共有4+ 2+1=7个乘客,分别是来自国家4,1,2,3共来自4个不同 的国家。


 

 因为这个题只记录在一艘船到达的时间前24小时内的人的国籍,这样有来的也有走的,先到的先走,所以是不是想到了一个数据结构呢?没错,用队列来模拟就好了,但是如果每一秒每一秒的来存,那么需要10^9的数组,很显然是不行的,这时候看到人的总和只有3×10^5,那么就按人来存好了,这样只需要3×10^5的数组,还要注意在24小时前的船只也要走,也就是不包括正好24小时前的。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define scn(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int N=3e5+10;
int n;
struct node
{
    int time;
    int p;
};
int gj[N];
queue<node>q;
int main()
{
    while(~scanf("%d",&n)){
            int ans=0;
            met(gj,0);
            int t,c;
            node m;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&t,&c);
            for(int j=1;j<=c;j++){
                 m.time=t;
                scanf("%d",&m.p);
                if(!gj[m.p])
                    ans++;
                 gj[m.p]++;
                 q.push(m);
            }
            while(1){
                node mm=q.front();
                if(mm.time+86400>m.time)
                    break;
                    gj[mm.p]--;
                    if(gj[mm.p]==0)
                        ans--;
                    q.pop();
            }
            printf("%d\n",ans);
        }
    }
}

 

 

D 回文日期

题目描述

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月 份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。

牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现 在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存 在的日期是回文的。

一个8位数字是回文的,当且仅当对于所有的i(1<= i <= 8)从左向右数的第i个 数字和第9-i个数字(即从右向左数的第i个数字)是相同的。

例如:

•对于2016年11月19日,用8位数字20161119表示,它不是回文的。

•对于2010年1月2日,用8位数字20100102表示,它是回文的。

•对于2010年10月2日,用8位数字20101002表示,它不是回文的。

每一年中都有1212个月份:

其中,1,3,5,7,8,10,121,3,5,7,8,10,12月每个月有31天;4,6,9,114,6,9,11月每个月有30天;而对于22月,闰年时有29天,平年时有28天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

1.这个年份是4的整数倍,但不是100的整数倍;

2.这个年份是400的整数倍。

例如:

•以下几个年份都是闰年:2000,2012,20162000,2012,2016。

•以下几个年份是平年:1900,2011,20141900,2011,2014。
 

输入

两行,每行包括一个8位数字。

第一行表示牛牛指定的起始日期。

第二行表示牛牛指定的终止日期。

保证date_i和都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。

保证date 1 —定不晚于date 2。

输出

一个整数,表示在date1和date2之间,有多少个日期是回文的。

样例输入

20110101
20111231

样例输出

1

提示

样例说明】

对于样例1,符合条件的日期是20111102。

对于样例2,符合条件的日期是20011002和20100102。

【子任务】

对于%60的数据,满足date1 = date2

 

这个题目直接模拟就好了,可能有一些麻烦,首先需要一个函数判断一下当前年份是否为闰年,还需要判断是否到达终止日期,

另外在就是判断回文了,并且我们可以看出每一年只可能有一个回文日期,所以当有一年有回文日期后,直接进入下一年即可

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
int x,y,yy,y2,m1,m2,d1,d2;
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};    //这里二月的天数写28,29都行,到时需要特判
int rn(int x)
{
    return ((x%4==0&&x%100!=0)||x%400==0)?29:28;
}
bool check()
{
    return (yy<y2||(yy==y2&&m1<m2)||(yy==y2&&m1==m2&&d1<=d2));
}
bool cz(int y,int m,int d)
{
    int s[8];
    int p=y*10000+m*100+d;
    for(int i=0;i<8;i++){
        s[i]=p%10;
        p/=10;
    }
    for(int i=0;i<4;i++)
        if(s[i]!=s[7-i])
        return false;
    return true;
}
int main()
{
    while(~scanf("%d%d",&x,&y)){
            int ans=0;
         yy=x/10000;y2=y/10000;   //年
         m1=(x/100)%100;m2=(y/100)%100;   //月
         d1=x%100;d2=y%100;        //日
        while(check()){
                bool falg=true;
            if(cz(yy,m1,d1)){
                ans++;
                yy++;
                m1=1;
                d1=1;
                continue;
            }
            if((m1==2&&d1==rn(yy))||m1!=2&&d1==day[m1]){
                m1++;
                d1=1;
                falg=false;
            }
            if(m1==13){
                yy++;
                m1=1;
                d1=1;
                falg=false;
            }
            if(falg)
                d1++;
        }
        printf("%d\n",ans);
    }
}

 

 

E 求和

一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。每个格子上都染了一种颜色color_i用[1,m]当中的一个整数表示,并且写了一个数字number_i。

定义一种特殊的三元组:(x,y,z),其中x,y,z 都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:

  1. xyz是整数,x<y<z,y-x=z-y
  2. colorx=colorz

满足上述条件的三元组的分数规定为(x+z) *(number_x+number_z)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以10,007所得的余数即可。
 

输入

第一行是用一个空格隔开的两个正整数n和m,n表纸带上格子的个数,m表纸带上颜色的种类数。 第二行有n用空格隔开的正整数,第i数字number表纸带上编号为i格子上面写的数字。 第三行有n用空格隔开的正整数,第i数字color表纸带上编号为ii格子染的颜色。

输出

共一行,一个整数,表示所求的纸带分数除以 10,007 所得的余数。

样例输入

6 2 
5 5 3 2 2 2 
2 2 1 1 2 1 

样例输出

82

提示

【输入输出样例 说明】  
纸带如题目描述中的图所示。  
所有满足条件的三元组为:(1,3,5),(4,5,6)。  
所以纸带的分数为(1+5)∗(5+2)+(4+6)∗(2+2) = 42+40 = 82。 

【数据说明】 
对于第 1 组至第 2 组数据, 1 ≤ n ≤ 100, 1 ≤ m ≤ 5,

对于第3 组至第 4 组数据, 1 ≤ n ≤ 3000, 1 ≤ m ≤ 100

对于第 5 组至第6组数据, 1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000且不存在出现次数超过20的颜色;

对 于 全 部 10 组 数 据 , 1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000,1≤color_i≤m,1≤number_i≤100000
 

首先看这个三元组, 分数的计算为(x+z) *(number_x+number_z)。

第一个条件x<y<z,y-x=z-y,我们可以看出x+z=2y。由此得出x,z同奇偶,因为一个偶数一个奇数是得不到一个偶数的。

然后看第二个条件,x,z的颜色要一样,那么我们可以把这一条纸带根据颜色和奇偶性分为2*m组,颜色相同且下标的奇偶性相同的就在一组。

接下来,我们只看其中一组的分数的求法,在这里 xi:代表这一组中第i个数的下标,yi:代表这一组第i个数代表的值是多少。

那么这一组的分数:

(x1+x2)*(y1+y2)+(x1+x3)*( y1+y3 )+......+(x1+xn)*(y1+yn)+

(x2+y3)*(y2+y3)+(x2+x4)*(y2+y4)+......+(x2+xn)*(y2+yn)+

......+

(xn-1+xn)*(yn-1+yn)。

,根据分配率展开,上面的第一行=(n-1)x1*y1+x1*(y2+y3+......+yn)+y1*(x2+x3+......+xn)+x2*y2+x3*y3+......+xn*yn,

然后下面的每一行,

行i=(n-i)*xiyi+xi*(y(i+1)+...yn)+yi*((xi+1)+......+xn)+x(i+1)*y(i+1)+......+xn*yn

还是先看第一行的,我们如果拿出一个x1*y1放入x1*(y2+y3+......+yn)中,就成了x1*(y1+y2+y3+......+yn),

再看y1*(x2+x3+......+xn),我们可以把这些依次放入下面的几行中,这样下面的行中xi×一系列y的就可以依次补全,

再把最后的x2*y2+x3*y3+......+xn*yn,也依次放入下面几行,

最后可以得出一个通项,对于每一个数i=(n-2)xi*yi+xi*(y1+y2+......+yn)。

over~

建议这个数学公式推导还是根据分奇偶的思路自己推一下,可能讲的不怎清晰。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
#define mod 10007
using namespace std;
const int N=1e5+10;
ll s[N][2],sum[N][2],a[N],color[N],n,m;
void init()
{
    met(s,0);
    met(sum,0);
    met(a,0);
    met(color,0);
}
int main()
{
    while(~scanf("%lld%lld",&n,&m)){
            init();
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++){
            scanf("%lld",&color[i]);
            s[color[i]][i%2]++;            //每一组中的数的数量
            sum[color[i]][i%2]+=a[i];                 //每一组中数的和,即y1+y2+......+yn
        }
        ll ans=0;
       for(int i=1;i<=n;i++){
         ans=(ans+(s[color[i]][i%2]-2)*a[i]*i%mod+i*sum[color[i]][i%2]%mod)%mod;
       }
       printf("%lld\n",ans);
    }
}

 

F 推销员

题目描述

阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。螺丝街是一条死胡同,出口 与入口是同一个,街道的一侧是围墙,另一侧是住户。螺丝街一共有 N 家住户,第 i 家住户 到入口的距离为 Si 米。由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的 距离相等。阿明会从入口进入,依次向螺丝街的 X 家住户推销产品,然后再原路走出去。   阿明每走 1 米就会积累 1 点疲劳值,向第 i 家住户推销产品会积累 Ai点疲劳值。阿明 是工作狂,他想知道,对于不同的 X,在不走多余的路的前提下,他最多可以积累多少点疲 劳值.

 

输入

第一行有一个正整数 N,表示螺丝街住户的数量。    接下来的一行有 N 个正整数,其中第 i 个整数 Si表示第 i 家住户到入口的距离。数据保 证 S1≤S2≤…≤Sn<1e8。    接下来的一行有 N 个正整数,其中第 i 个整数 Ai表示向第 i 户住户推销产品会积累的 疲劳值。数据保证 Ai<1e3。 

 

输出

输出 N 行,每行一个正整数,第 i 行整数表示当 X=i 时,阿明最多积累的疲劳值。 

 

样例输入

5  
1 2 3 4 5  
1 2 3 4 5

 

样例输出

15  
19  
22 
24 
25

 

提示

【输入输出样例  说明】 
X=1: 向住户 5 推销,往返走路的疲劳值为 5+5,推销的疲劳值为 5,总疲劳值为 15。   
X=2: 向住户 4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值为 4+5,总疲劳 值为 5+5+4+5=19。 
X=3: 向住户 3、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值 3+4+5,总疲 劳值为 5+5+3+4+5=22。  
X=4: 向住户 2、3、4、5 推销,往返走路的疲劳值为 5+5,推销的疲劳值 2+3+4+5, 总疲劳值 5+5+2+3+4+5=24。  
X=5: 向住户 1、 2、 3、 4、 5 推销,往返走路的疲劳值为 5+5,推销的疲劳值 1+2+3+4+5, 总疲劳值 5+5+1+2+3+4+5=25。 

 

一道贪心的题目,每一次都是在上一次的情况下进行更新,一开始的思路是先找到从起点开始到那n个点中总疲劳值最大的,在分别看往左走和往右走哪个疲劳度消耗的更多,注意在这种情况下:

向左走只需要加上推销的疲劳值,因为他已经走过这里了,就等于顺路推销

向右走的话需要 当前点的距离减去他之前的点的距离×2+推销当前的花费,也就是(a[i].dis-a[now].dis)*2+a[i].w。在更新一下now,就是那个当前最远到达的点。

当向左走和向右走花费一样的时候,无所谓哪个了,都可以的。

把向左走和向右走的比较一下就好了,但是不知道什么原因在洛谷只过了30......orz

然后想到向左走的的用大根堆优化一下(优先队列),,只需要计算向右走的就好了。

具体看代码。。。

#include<bits/stdc++.h>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ll long long
#define pb push_back
#define PI  acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x)  priority_queue<x>
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int N=1e5+10;
int n;
priority_queue<int>q;
struct node
{
    int dis;
    int w;
}a[N];
int main()
{
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)
            sc(a[i].dis);
        for(int i=1;i<=n;i++)
            sc(a[i].w);
        int now=0;
        q.push(0);
        int ans=0;
        for(int i=1;i<=n;i++){
            int p=q.top();
            int pp=now;
            int maxn=0;
            for(int i=now+1;i<=n;i++)
                if((a[i].dis-a[now].dis)*2+a[i].w>maxn){
                    pp=i;
                    maxn=(a[i].dis-a[now].dis)*2+a[i].w;
                }
                if(maxn>p){         //比较是否向右走比向左走花费高
                    q.push(maxn);       //高就讲右边的入队
                for(int i=now+1;i<pp;i++)
                    q.push(a[i].w);
                now=pp;
                }
                ans+=q.top();        //更新答案
                q.pop();
                printf("%d\n",ans);
        }
    }
}
/*
5
1 2 2 4 5
5 4 3 4 1
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值