2019暑期组队排位总结

2019暑期组队排位总结

  • 2019牛客暑期多校(第一场)
    • problem J
    • problem A
    • problem E
  • 2019牛客暑期多校(第二场)
    • problem F
    • problem H
  • 2018 ICPC Asia Singapore Regional
    • problem J
    • problem L
    • problem C
    • problem F
  • 2019牛客暑期多校(第三场)
    • problem B
    • problem H
    • problem F
  • 2019牛客暑期多校(第四场)
    • problem K
    • problem J
    • problem A
  • 2019牛客暑期多校(第五场)
    • problem K
    • problem B
    • problem G
  • 2018-2019, ICPC, Asia Yokohama Regional Contest 2018
    • problem A
    • problem B
    • problem C
    • problem G
  • 2018-2019 ACM-ICPC, Asia Seoul Regional Contest
    • Problem D
    • Problem F
    • Problem L
  • 2019牛客暑期多校(第六场)
    • problem A
    • problem B
    • problem D
    • problem J
  • 2019牛客暑期多校(第七场)
    • problem A
    • problem B
    • problem D
    • problem J
    • problem C
  • 2019牛客暑期多校(第八场)
    • problem G
  • 2017-2018 ACM-ICPC, Asia Tsukuba Regional Contest
    • problem A
    • problem B
    • problem C
    • problem I
  • 2018-2019 ACM-ICPC Southeastern European Regional Programming Contest
    • problem B
    • problem E
    • problem C
  • 2018 ICPC East-Central NA Regional Contest
    • problem A
    • problem B
    • problem C
  • 2018 ACM-ICPC North Central North America Regional Contest
    • problem E
    • problem C
    • problem F
    • problem G
    • problem I
    • problem B
    • problem J
  • 2016 ICPC Mid-Central USA Region
    • problem B
    • problem A
    • problem C
    • problem G
    • problem I
  • 2018 CCPC吉林赛区
    • problem A
    • problem B
    • problem C
    • problem D
  • 2018 Rocky Mountain Regional Contest
    • problem A
    • problem G
    • problem D
    • problem J
    • problem C
    • problem E
    • problem H
  • 2019牛客暑期多校(第九场)
    • problem D
    • problem E
  • 2019牛客暑期多校(第十场)
    • problem J
    • problem E
    • problem B
    • problem D
    • problem F

Wiki:戳这里
我还是习惯性在博客也开一篇总结方便看吧哈哈哈哈

2019牛客暑期多校(第一场)

problem J

题意:给你两个分数x/a,y/b,要你比较他们大小。分子范围为1e18,分母范围为1e9。

题解:直接交叉相乘会爆ll,所以先把两个数的整数部分求出来作比较,如果整数部分一样,再把分子%分母,这样两个分数交叉相乘就不会爆ll,可以比较了。

problem A

题意:两个数组定义为相等的,当且仅当从两个数组中任选一个区间[l,r],他们的最小值的下标是一样的,给你两个数组u和v,问你最大的下标p,使得1到p中任选一个区间,他们的最小值下标是一样的。

证明:假设遍历到第i个元素,也就是前i-1个元素都是符合条件的,那么我们需要考虑的新区间就是以第i个元素为右端点的所有区间,现在我们考虑以第i个元素为最小值的区间最左能延伸到哪里,如果两个数组最左能延伸到的位置,即栈大小是一样的,就说明是符合条件的。

题解:两个数组分别用一个单调递增的栈维护,当且仅当遍历到第i个元素,两个栈的size一样,ans = p,否则,直接break。

代码:戳这里

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int x[maxn],y[maxn];
 
int main()
{
     
    int n;
    while(~scanf("%d",&n))
    {
     
        for(int i=1; i<=n; i++)
        {
     
            scanf("%d",&x[i]);
        }
        for(int i=1; i<=n; i++)
        {
     
            scanf("%d",&y[i]);
        }
        stack<int> a,b;
        int ans = 1;
        for(int i=1; i<=n; i++)
        {
     
            while(!a.empty() && x[i] < a.top())
            {
     
                a.pop();
            }
            a.push(x[i]);
            while(!b.empty() && y[i] < b.top())
            {
     
                b.pop();
            }
            b.push(y[i]);
            if(a.size() != b.size())
                break;
            else
                ans = i;
        }
        printf("%d\n",ans);
    }
}

problem E

题意:一个2*(n+m)长度的只包含“A”“B”的序列,可以分解成n+m个子序列,其中有n个“AB”,m个“BA”,给你n和m,问你符合条件的序列有多少个。

题解:对于一个序列从前往后遍历过去,遇到一个“A”,肯定会优先把它当成“AB”的“A”,再考虑它是“BA”的“A”,遇到一个“B”同理。那么我们可以有dp[x][y]表示一共有x个“A”,y个“B”,当x或y至少有一个为0,此时方案数只有1。根据之前的贪心策略,任一时刻不同时满足y-x<=m和x-y<=n的话,方案数为0。这样,一个记忆化搜索的dp就很好写出来了。

代码:戳这里

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn = 2005;
const int mod = 1e9+7;
int n,m;
ll dp[maxn][maxn];

ll solve(int x,int y)
{
     
    if(y - x > m || x - y > n)
        return 0;
    if(x == 0 || y == 0)
        return 1;
    if(dp[x][y])
        return dp[x][y];
    dp[x][y] = solve(x-1,y) + solve(x,y-1);
    dp[x][y] %= mod;
    return dp[x][y];
}

int main()
{
     
    while(~scanf("%d%d",&n,&m))
    {
     
        for(int i=0; i<=n+m; i++)
        {
     
            for(int j=0; j<=n+m; j++)
            {
     
                dp[i][j] = 0;
            }
        }
        printf("%lld\n",solve(n+m,n+m));
    }
}

2019牛客暑期多校(第二场)

problem F

题意:给定一个N*N矩阵,令第i行j列为a[i][j],在此可理解为i和j的价值。题目要求把1–>N人分成两组,且当i和j不同组时,价值生效,否则价值为0。求分组后的最大总价值。 !!! 1<=N<=14 !!!

题解:范围不大,可暴力。 dfs模拟分组。对于当前的i,若数组A人数不够,进A组,并更新当前总价值(即加上i和B组所有人的价值)进入下一次dfs。对于B组操作也一样。

problem H

题意:给你一个只包含0和1的n×m大小的矩阵,问你第二大的全为1的矩形有多大。(不严格第二大)

题解:每个矩阵的大小由长和宽决定,矩形的长对应原矩阵中每一行最多有多少个元素,他们向上扩展的连续“1”的数量一样,矩形的宽对应向上扩展的连续“1”的数量。我们从第一行开始枚举,一开始先更新该行所有元素向上扩展最多有多少个连续“1”,设为c[j]数组,然后用一个单调递增的栈维护该行的c[j]数组,找出该行每个元素以自己为最小值,向左向右分别最远扩展到哪里,这里就有l[i],r[i]数组,此时最大的矩形面积应为(r[i]-l[i]+1)* c[j],第二小的值是max((r[i]-l[i])* c[j],(r[i]-l[i]+1) * (c[j] - 1)),然后不断更新最大值和第二大值,最后的第二大值就是答案。

代码:https://pastebin.ubuntu.com/p/BHfz5g4sPz/

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 1005;
char a[maxn][maxn];
int c[maxn],l[maxn],r[maxn];
 
void update(int &mx,int &mmx,int x)
{
     
    if(x <= mmx)
        return;
    if(x <= mx)
    {
     
        mmx = x;
        return;
    }
    mmx = mx;
    mx = x;
}
 
int main()
{
     
    int n,m;
    scanf("%d%d",&n,&m);
    int mx = 0,mmx = 0;
    for(int i=0; i<n; i++)
    {
     
        scanf("%s",a[i]);
    }
    for(int i=0; i<n; i++)
    {
     
        for(int j=0; j<m; j++)
        {
     
            if(a[i][j] == '1')
                c[j]++;
            else
                c[j] = 0;
            l[j] = j;
            r[j] = m-1;
        }
        stack<int> s;
        for(int j=0; j<m; j++)
        {
     
            while(!s.empty() && c[j] <= c[s.top()])
            {
     
                l[j] = l[s.top()];
                r[s.top()] = j-1;
                s.pop();
            }
            s.push(j);
        }
        int mx1 = 0,mmx1 = 0;
        for(int j=0; j<m; j++)
        {
     
            int x = r[j] - l[j] + 1,y = c[j];
            update(mx1,mmx1,x*y);
            update(mx1,mmx1,(x-1)*y);
            update(mx1,mmx1,x*(y-1));
        }
        update(mx,mmx,mx1);
        update(mx,mmx,mmx1);
    }
    printf("%d",mmx);
}

2018 ICPC Asia Singapore Regional

problem J

签到题

problem L

题意:定义函数NPF(i)为i的非素数因子个数,有q个询问,输出NPF(n)(q范围为3e6,n范围为2e6)ps:1也是非素数

题解:预处理出1到2e6的非素数表,然后遍历1到2e6,如果是非素数,就ans[i*j]++。每个询问,直接输出ans[n]就好了。

代码:https://pastebin.ubuntu.com/p/gtYBzBmbjV/

problem C

阅读题

problen B
题意:n个点,m条边,如果有(u,v),(v,w),那么u点可以传染w点,问你至少要添加多少条边,才能使无论从哪条边出发,都能感染所有点。

题解:当有一个奇环的时候,就能符合题意。所以我们先找出有多少个联通块,ans = 联通块数量-1,如果图中本来不存在奇环,就ans再++。判是否存在奇环用二分图染色。

代码:https://pastebin.ubuntu.com/p/S9k6zx58XJ/

problem F

题意:要你找两个数A,B,使得它们在序列中排列是ABAB,如果有多对,找字典序最小的。

题解:我们假设这四个数在序列中的位置是x1,x2,y1,y2,那么我们可以枚举每一对x2,y2(在枚举前先从后往前扫,预处理每一个a[i]对应的位置),因为要字典序最小,我们要找符合条件的最小的y1,y1对应的数就是A,x2对应的数就是B。涉及区间最值,我们用线段树,一开始树的每个结点都是INF,然后每枚举完一对x2,y2,就把y2对应的数更新到树上,因为y2有可能成为之后的y1。

代码:https://pastebin.ubuntu.com/p/kwSVT7Ymxb/

2019牛客暑期多校(第三场)

problem B

题意:给你一个01序列,要你找最长的01数量相等的子串和子序列的长度分别是多少(子串是要求连续的,子序列可不连续)

题解:子序列最大长度直接是min(0的数量,1的数量)*2。而01数量相等的最大长度的子串我们假设是区间[l,r],(num0,num1分别是0、1数量的前缀和)那么有num0[r] - num0[l-1] = num1[r] - num1[l-1],移项得num0[r] - num1[r] = num0[l-1] - num1[l-1],即某两个点的01数量的差相等。我们可以用pos[i]来存01数量差值为i的最早出现的位置,因为差值可能为负,因为给出的序列长度为1e5,处理pos[i]的时候,应把i加上1e5,最后pos[1e5] = 0。然后遍历序列,如果pos[num0[i]-num1[i]+1e5]不为0,ansa = max(ansa,i-pos[k]),如果num0[i] - num1[i]为0的话,ansa = max(ansa,i)。

代码:https://pastebin.ubuntu.com/p/8QX4HmDpck/

problem H

题意:给你n个在平面上的点的坐标,要你找一条直线,使得这条直线左右两边的点的数量一样。(n一点是偶数。答案的直线用两个点表示,且它不能穿过给出的点)

题解:先把n个点按横坐标由小到大排序。如果第n/2个点和第n/2+1个点的横坐标不一样,那么这条直线的两个点分别是(a[n/2].x,-inf),(a[n/2+1].x,inf)。否则,我们找一条直线,刚好不穿过最中间的那个点,且在那个点下面。我们考虑假设这条直线一个点是(a[n/2].x-1,-inf),如果它穿过最中间的那个点,那另一个点会是(a[n/2].x+1,inf+2*a[n/2].y),现在,我们要让这条直线不穿过最中间那个点,只需让第二个点的纵坐标下移一点就ok了,这样斜率会比原来小,绝对不会穿过最中间那个点。

代码:https://pastebin.ubuntu.com/p/Wx8dym9Yst/

problem F

题意:给你n×n的矩形,要你找一个最大的矩形,使得矩形内最大值与最小值之差不超过M。

题解:枚举矩形的上下边界,同时维护对于当前枚举的上下边界的每列最大最小值(用mi[j],mx[j]数组表示)。枚举矩形右边界,用一个单调递减队列维护mx[j]的最大值,用一个单调递增队列维护mi[j]的最小值,就可以知道可行的最左边界。(非常鬼畜,要手模拟队列+快读才不会t)

代码:https://pastebin.ubuntu.com/p/M2J97tyhbV/

2019牛客暑期多校(第四场)

problem K

题意:给你一个串,要你找是300的倍数的子串有多少个(“0,00……也算”)

题解:num[i][j]表示前i个字符%3为j的子串一共有多少个。一开始num[0][0] = 1,遍历整个串,cnt为前i个字符组成的十进制数%3,如果前面有两个连续0,那么ans += num[i-3][cnt];如果前面刚好是“00”,ans++;如果前面是“0”,ans++。然后更新num[i][j]数组。

代码:https://pastebin.ubuntu.com/p/xNQysFmQqP/

problem J

题意:n个点,m条边,起点s,终点t,可以最多选k条边免费,问你s到t的一共最小花费多少。

题解:多层图最短路板子题。dis[i][j]表示起点s到点i最多选j条边的最小花费。优先队列q(u,dist,d)分别表示点i,s到i的最小花费,目前已用d条免费边。松弛的时候考虑这条边要不要变成免费边(当然,变成免费边的前提是d+1<=k),如果不变成免费边,dis[v][d] = dis[u][d] + w,q.push({v,dis[v][d],d});如果变成免费边,dis[v][d+1] = dis[u][d],q.push({v,dis[v][d+1],d+1})。最后从dis[t][1]到dis[t][k]里取个min。

代码:https://pastebin.ubuntu.com/p/pV6fRXnbr5/

problem A

题意:n个点,n-1条边,有k个目标点,要你从图上找一个点u,使得u到所有目标点距离之和最小,求点u到目标点中的最大值。

题解:考虑距离最远的两个关键点,设它们的距离为d,d/2上取整即为答案。

必要性:这两个人要碰面,必然要走至少d/2步。
充分性:我们取两人路径中和一头距离为d/2上取整的一个点,让所有人在这相聚。如果有一个人在d/2时间内到不了,那么它和路径两头中与它远的那一头的距离大于d,与最远的假设矛盾。
找最远的两个点相当于找树的直径,可以用两次dfs。

代码:https://pastebin.ubuntu.com/p/7cWsR7B8z5/

2019牛客暑期多校(第五场)

problem K

题意:给你n,要你找m,使得m是n的倍数,而且m各个位的数字加起来也是n的倍数,m最多不能超过1e4位。

题解:把n打印n次就好。

problem B

题意 求广义斐波那契第n项模p。 n范围很大,可达10的1e6次方,但是p范围是1e9到2e9。

一、

可用矩阵快速幂,但是不能加上欧拉降幂!!欧拉降幂不适用于矩阵。 接下来考虑一下n,对于求a的n次幂通常是用快速幂就可以了,但是这里的n太大了,所以…

关于指数16的处理,对比一下快速幂和题解方法好了

①指数16=(22)(2*2)

②指数16=61+110

第一种是logn(快速幂),第二种则是指数的位数(题解)。 然后就可以啦!(矩阵用vector存会t_t)

二、

有看到别人貌似是求循环节过的,先码着。

①求ans,对于p的质因子i,ans=(i+1)*(i-1)。

②然后对于n,进行对ans取模操作,再进行矩阵快速幂。

problem G

题意:给你一个串s,一个串t,要你从s中找有多少个子序列大于t的。(两个串的长度都不超过3000)

题解:对于位数比t大的子序列,统计时直接用组合数。组合数C(n,m) = n!/(n-m)!*m!。预处理出1到3000的阶乘和阶乘的逆元。对于位数和t一样的,用dp。dp[i][j]表示从第i位开始往后取j位,有多少子序列比t大。那么答案就会加上dp[1][m],dp[2][m]……dp[n-m+1][m]。ch = t[m-j+1],如果s[i]ch,dp[i][j] = C(n-i,j-1);如果s[i]=ch,dp[i][j] = sum(dp[k][j-1])(i

代码:https://pastebin.ubuntu.com/p/twNjrF9wN5/

2018-2019, ICPC, Asia Yokohama Regional Contest 2018

problem A

题意:给你一个串s0,再给你若干个串t,要你判断串t应该排在s0的前面还是后面,(-代表前面,+代表后面,一样也是输出+)。排序规则:①数字排在字母前面 ②都是字母的话按ASCII码排序 ③都是数字的话,要把连续的数字提取出来转成十进制数,再比较。

题解:对于s0的每个字符:一、数字 ①串t不是数字,return true ②串t也是数字,把两个串的数字都提取出来,比较。 二、字母 ①串t是数字,return false ②串t是字母 看哪个字典序大。三、当s0都遍历完,t还没到尾,return true。四、遍历s0过程中,串t就到尾了,return false。(在cctype头文件中有isdigit函数判断一个字符是否是数字,isalpha函数判断一个字符是否是字母。)

代码:https://pastebin.ubuntu.com/p/tH8mDWcb8r/

problem B

题意:给你一个序列,要你挑出最多的数,使得他们是等差数列。(不一定是子序列)

题解:dp[i][j]表示包含a[i]、a[j]两项的等差数列的长度,我们用mapmp存每个a[i]的位置,然后枚举i j,看是否存在tmp = 2*a[i]-a[j](即包含a[i]、a[j]的等差数列的前一项),注意,这里查看是否存在tmp要用mp.count(tmp),不能用mp[tmp]是否为0来判断,否则会mle,因为前者只是查询,后者如果查询到没有,会有插入操作。如果存在,dp[i][j] = dp[tmp][i] + 1,ans = max(ans,dp[i][j])。如果不存在,dp[i][j] = 1。

代码:https://pastebin.ubuntu.com/p/7ZrhDXXKnt/

problem C

题意:有r行座位,中间有过道,过道两边分别有s列座位,在(r,s+1)的位置,有个出口,有p个乘客,问所有乘客都逃出出口的最小时间。(每一时间单位只能移动一格,如果前面有人堵着就得一直等)

题解:逃出出口和从出口去到自己位置是一样的,想要时间最小,肯定要让离得远的先走,所以先按乘客离出口的步数由大到小排序。ans一开始是a[1].d(最远的乘客的步数),然后如果后面有乘客所需走的步数+已走步数小于当前的ans,ans = 乘客所需走的步数+已走步数。

代码:https://pastebin.ubuntu.com/p/RpZV8ntFyt/

problem G

题意:给你一个序列,问你把它变成单峰序列最小要交换多少次,每次交换只能发生在相邻的两个数之间。

题解:贪心。对于单峰序列,肯定是小的数放外面,大的数放中间,无论先移哪个数,最后肯定都是要移到他应该在的位置,所以应该先移哪个数字不重要。那我们从小到大移动好了,我们先预处理出每个a[i]的位置,然后从小到大遍历每个数字,如果这个数字存在在序列中,l是这个数字在序列中的第一个位置,r是这个数字在序列中的最后的位置,当这个数字都没移好,我们都比较pos[i][l]移到最左边的步数tmp1,pos[i][l]移到最右边的步数tmp2,pos[i][r]移到最左边的步数tmp3,pos[i][r]移到最右边的步数tmp4,每次移tmp值最小的那个。但是怎么知道它移到最左和最右一共要多少步呢?只要知道序列中有多少个数比它小(在它左边),就知道移到最左的步数,知道有多少个数比它大(在它右边),就知道移到最右的步数。最后再在树状数组中更新,表示这个数已经移好。

代码:https://pastebin.ubuntu.com/p/9ySZcr5p8c/

2018-2019 ACM-ICPC, Asia Seoul Regional Contest

Problem D

签到题

Problem F

题意:给你一个表达式,让你判断是否合法,如果合法就判断是否有括号多余或缺少(给出的表达式会有空格,用gets输入就好)。

题解:用栈模拟,遇到左括号,字母,运算符就入栈,并分别记录他们的数量,遇到右括号就开始pop,并记录出栈过程中遇到多少字母和运算符,字母数量应为2,运算符数量应为1,直到遇到左括号或栈空,如果栈空都没有遇到左括号就error,如果字母数量-运算符数量=1,且字母数量大于2,用一个变量记录,flag=1,并把这一整个()的内容改成一个字母,push进栈,遍历完整个字符串,以下情况是error:①左括号数量不为0②在遍历过程中存在某个时刻运算符数量比字母数量多③最后剩下的不是两字母一运算符。如果都没有上述情况,则以下情况是improper:①最后栈里size大于3,字母数量-运算符数量=1。②出栈过,且flag为1。剩下的就是proper了。

字符串大模拟,本来也码了半天,想了很多细节情况了,最后还是WA了就丢给队友做了,总共是三个人死磕了18发终于过了,队友NB

Problem L

题意:一个工程需要n天完成,每天需要per[i]个人,有m个人,给出每个人在n天后希望自己能工作的天数,每个人每次工作连续w天,工作完之后休息h天,可继续工作。问是否能有一个工作安排表,满足工程能顺利完成,且每个人的愿望达成。

题解:一个结构体,存每个人的编号id,想工作的天数day,下一次工作最早可以什么时候开始next。安排工作的时候,优先安排想工作天数多的人先工作,这里可以用一个优先队列存下来,day-=w,next=i+w+h,如果day不为0,就放入休息队列。遍历每一天,看需要新派多少人,如果当天没有人可以派或者派的人工作w天后的日期大于n就是不满足情况的。遍历完n天之后,如果两个队列不是都为空,也是不满足的。(还有一个点需要注意,每天需要派出的人并不就是per[i],因为每个人连续工作w天,所以在遍历日期前先把per[i]减掉前w天的per[i])

最后7分钟居然AC了 太感动了。说实话第一次运用优先队列做题。。不太熟悉,加上本身数据结构知识薄弱啊,一开始做懵了,后来才发现自己有地方犯蠢了。算是重大突破了(不
↓队友改良后的代码 本来ans我直接用二维数组存了 vector更好

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn = 2005;
int m,n,w,h;
int per[maxn];//每天需要工作的人数

struct node
{
     
    int id,day,next;
    node(int i,int d,int n)
    {
     
        id = i;//编号
        day = d;//想工作的天数
        next = n;//下一次开始工作的天数
    }
    bool operator < (const node& r)const
    {
     
        return day<r.day;
    }
};

priority_queue<node>Q;//待工作队列
queue<node>q;//休息队列
vector<int> ans[maxn];

int main()
{
     
    scanf("%d%d%d%d",&m,&n,&w,&h);
    for(int i=1; i<=m; i++)
    {
     
        int d;
        scanf("%d",&d);
        Q.push(node(i,d,1));
    }
    for(int i=1; i<=n; i++)
    {
     
        scanf("%d",&per[i]);
    }
    for(int i=1; i<=n; i++)//因为每个人连续工作w天,所以每天新派出的人数会比真正的少
    {
     
        for(int j=i+1; j<=i+w-1; j++)
        {
     
            per[j] -= per[i];
        }
    }
    int flag = 1;
    for(int i=1; i<=n; i++)
    {
     
        while(!q.empty() && q.front().next == i)//如果到这一天可以开始工作,把他从休息队列放到工作队列
        {
     
            node u = q.front();
            q.pop();
            Q.push(u);
        }
        while(per[i])//还需要新派的人数不为0
        {
     
            if(Q.size() == 0)//没有想工作的人
            {
     
                flag = 0;
                break;
            }
            node u = Q.top();
            Q.pop();
            if(i+w-1 > n)//在n天内没有实现自己想工作的天数
            {
     
                flag = 0;
                break;
            }
            ans[u.id].push_back(i);
            u.day -= w;
            u.next = i+w+h;
            if(u.day != 0)//想工作的天数还大于0,放入休息队列
                q.push(u);
            per[i]--;
        }
        if(!flag)
            break;
    }
    if(!q.empty() || !Q.empty())//Q,q不为空表示还有人没有达到自己愿望
        flag = 0;
    if(!flag)
        printf("-1");
    else
    {
     
        printf("1\n");
        for(int i=1; i<=m; i++)
        {
     
            for(int j=0; j<ans[i].size(); j++)
            {
     
                printf("%d ",ans[i][j]);
            }
            printf("\n");
        }
    }
}

2019牛客暑期多校(第六场)

problem A

题意:给你一个字符串s,仅包含小写字母,再给你一个长度为26的字符串t,每一位代表每个字母的属性,属性分别有h、w、d。当字符串中有多于25%的h,输出Harmful;否则当字符串中少于10%的h,输出Recyclable;否则当d的数量大于等于w的两倍,输出Dry;否则,输出Wet。

题解:len代表字符串s的长度,len1=ceil(len1.00.25),len2=floor(len1.00.1),变量h、w、d分别代表字符串s串属性为h、w、d的总数。①h>=len1,Harmful②h<=len2,Recyclable。③d>=2*w,Dry。④直接输出Wet。

problem B

模拟题

题目要求把一个128位的二进制转换成32位的十六进制,再每4位成一个段。段与段之间用一个冒号隔开。每个段如果有前导零,前导零可忽略不写。若存在有几个段连在一起(连续),且他们都是0的情况,可用双冒号代替,但是这种双冒号在一个串中最多只能出现一次。 给一个串,求其最短的表示,若最短的情况有多个,输出字典序最小的那个。

题解:先按他说的做,转换成16进制后,找最长的连续0的段,按题目要求,所以若当前的连续0段长度 >= 临时最大值时,更新。

应该注意的是,相同数目的‘0’会因其位置(最前面、中间、最后)不同而缩短的长度不同(因为长度也包括冒号呀),之后再输出。

problem D

题意:给你n个物体,每个物体有自己的体积,再给你k个箱子,每个箱子体积相同。问:每个箱子体积最少为多少,可以装下所有物品。

题解:找到答案下界sum/k,上界为sum/k+maxV

P.S.

找上界的官方题解说明:假设某个答案ans装不下,那么每个箱子的剩余空间都

此时k*(ans-maxV+1)<=sum

所以,ans<=sum/k+maxV-1

数据范围和时间限制都比较宽松,所以直接在这个范围内暴力枚举答案就完事了。

坑:答案没有单调性。如

15 5

39 39 39 39 39 60 60 60 60 60 100 100 100 100 100

199为一个合法的答案,但200不是,201也不是。

话说本来我用了优先队列模拟箱子,把物品从大到小排序,箱子优先让目前装的物品体积小的去装。不知道为啥WA了呜呜呜,大概还是有些情况不准确吧。。。。T_T
代码:https://pastebin.ubuntu.com/p/tqqgDGyBHt/

#include 
#include 
#include 
using namespace std;
int n,k;
int item[1005];
int box[1005];
bool check(int a)
{
     
    int i,j;
    for(i=0;i<k;i++)box[i]=a;
    for(i=n-1;i>=0;i--)
    {
     
        for(j=0;j<k;j++)
        {
     
            if(item[i]<=box[j])
            {
     
                box[j]-=item[i];
                break;
            }
        }
        if(j==k)return false;
    }
    return true;
}
int main()
{
     
    int t,i,j,sum;
    scanf("%d",&t);
    for(i=1;i<=t;i++)
    {
     
        scanf("%d %d",&n,&k);
        sum=0;
        for(j=0;j<n;j++)
        {
     
            scanf("%d",&item[j]);
            sum+=item[j];
        }
        sort(item,item+n);
        for(j=max(sum/k,item[n-1]);!check(j);j++);
        printf("Case #%d: %d\n",i,j);
    }
    return 0;
}

problem J

题意:有n种技能,每种技能有0-m级,每升一级花费的钱为cij,当所有技能都升为j级,可获得dj钱,(cij,dj均有正有负),问你最大的收益是多少。

题解:c[i][j]是第i种技能升到j级一共所需花费(花费为负代表有收益),d[i]代表所有技能升到i级收益前缀和。从后往前,把每个c[i][j]覆盖成c[i][j]到c[i][m]的最小值(即cc数组)。csum[i]是cc[1][i]到cc[n][i]的前缀和,即所有技能升到i级所需最小花费(对于每一技能,升到i级所需最小花费不一定就是升到i级的前缀和,可能往后升级更优,故有前面所说从后往前覆盖这一操作)。枚举每一级数i,ans=max(d[i]-csum[i]-(c[j][i]-cc[j][i]))。因为所有技能升到i级就会有一个收益,所以枚举时要保证不会出现所有技能都往后升到大于i级以便获得更优收益,我们应选一个覆盖前后差值最小的技能,使之不要升到i级之后,故有-(c[j][i]-cc[j][i])。(有一个坑就是枚举级数应从0开始,因为有可能di一直是负很大的,还有ans初始化应为0)

代码:https://pastebin.ubuntu.com/p/CznkHBJKSG/

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const long long INF = 0x3f3f3f3f3f3f3f3f;
long long cc[1005][1005];
long long c[1005][1005];
long long csum[1005];
long long d[1005];
int main()
{
     
    int t,haha,n,m,i,j;
    long long ans,dd;
    scanf("%d",&t);
    for(haha=1;haha<=t;haha++)
    {
     
        ans=0;
        scanf("%d%d",&n,&m);
        memset(c,0,sizeof(c));
        memset(csum,0,sizeof(csum));
        memset(cc,0,sizeof(cc));
        memset(d,0,sizeof(d));
        for(i=1;i<=n;i++)
        {
     
            for(j=1;j<=m;j++)
            {
     
                scanf("%lld",&c[i][j]);
                c[i][j]+=c[i][j-1];
            }
            dd=INF;
            for(j=m;j>=0;j--)
            {
     
                if(c[i][j]<dd)dd=c[i][j];
                cc[i][j]=dd;
                csum[j]+=dd;
            }
        }
        for(i=1;i<=m;i++)
        {
     
            scanf("%lld",&d[i]);
            d[i]+=d[i-1];
        }
        ans=0;
        for(i=0;i<=m;i++)
        {
     
            for(j=1;j<=n;j++)
            {
     
                ans=max(d[i]-csum[i]-(c[j][i]-cc[j][i]),ans);
            }
        }
        printf("Case #%d: %lld\n",haha,ans);
    }
    return 0;
}

2019牛客暑期多校(第七场)

problem A

题目要求把一个大串分成最少份,且分成的每一个小串都是该小串循环时字典序最小的那个。

大串只由01组成,要求输出分完后满足要求的样子。

题解:

对于每个当前起点L(L

暴力判断在大串下标在[L,R]之间组成的小串是否符合要求。

找出起点为L时最大的右端点R,这样,大串中下标从L到R便自成一小串。

最后更新L=R+1。

!!如何判断小串合不合要求: 小串s0=substr(L,R-L+1);

运用string的substr(poi,n),poi为起点,n为个数。

枚举i={i|0<=i<=r-l},

string tmp=s.substr(L+i,R-L-i+1)+s.substr(L,i),

找出所有串中的最小的那个,和当前小串s0比较大小。

problem B

题目给出一个n次多项式,问可不可化简,可以的话就No,不可以就输出Yes.

题解:

输出"Yes"当仅当n1或者n0,或者n==2、bb<4a*c成立的时候;

其他情况就都"No"。

problem D

题意:给你一个数n,一个素数p,要你输出一个n位数,使得它是p的倍数。

题解:如果np,输出p,并在后面补(n-p)个0。

problem J

题意:定义f(x)为x倒过来的数,给你x,y,求f(f(x)+f(y))。

题解:x、y的输入用字符串,用reverse函数倒过来之后转成int类型,再直接相加,相加的数一直%10,变成字符串,再输出第一个非0数到len之间的数。(wa了一次因为答案字符串没清零。。。)

代码:https://pastebin.ubuntu.com/p/kYPqrbD2Gz/

problem C

题意:有n种树,每种树给出高度h,砍掉每颗树的花费c,每种树的数量p,现在要砍掉一些树,使得最高的树的数量超过所有树的一半,问最小花费。(不同种类的树高度可能相同)

题解:枚举不同的高度,把高于它的树都砍掉,然后比它矮的树挑便宜的砍,使得该高度的树占所有树的1/2+1。给树按高度排序,首先可以用后缀和预处理出砍掉高于每种高度的树的花费,然后用线段树维护某一价格区间的树一共有多少颗,tr[1].num就是比当前高度矮的树的总数,要砍掉的树的数量就是tr[1].num-tot+1,tot是当前高度的树的总数量。询问砍掉矮的树的花费时,当要砍的树的数量k小于左子树,就递归到左子树,如果大于左子树,就花费加上左子树的花费,数量k减去左子树的数量,然后递归到右子树。当递归到叶子节点的时候,花费直接加上剩余数量*该价格。

代码:https://pastebin.ubuntu.com/p/hV6bQCPsBB/

2019牛客暑期多校(第八场)

problem G

题意:给你一个字符串,每次可以把连续的三个一样的字符删去,然后剩下的合并,问最多能删多少次。(字符串只包含大写字母)

题解:用一个栈维护整个字符串,每遍历一个字符,如果栈中size大于等于2,就pop两个出来,看看这三个是否一样,如果不一样,就push回进去。

代码:https://pastebin.ubuntu.com/p/YhQTcv2t4T/

2017-2018 ACM-ICPC, Asia Tsukuba Regional Contest

problem A

题意:有1cm、kcm的黑色的积木和1cm的白色的积木,问积木高度不超过lcm时,一共有多少种搭积木方案(最下和最上一块必须是黑色,必须黑白相间,至少有一块积木)

题解:dp[i][0]表示当前高度为i,且最上一块是白色。p[i][1]表示当前高度为i,且最上一块是黑色。一开始dp[1][1] = dp[k][1] = 1。假设当前积木高度为i,那么可以有dp[i+1][0]表示搭一块白色的上去,dp[i+1][1]表示搭一块1cm黑色的上去,dp[i+k][1]表示搭一块kcm黑色的上去。因为要黑白相间,所以dp[i+1][0] += dp[i][1],dp[i+1][1] += dp[i][0],dp[i+k][1] += dp[i][0]。最后答案就是把各个高度下最后一块是黑色的答案加起来。

代码:https://pastebin.ubuntu.com/p/NFnyvjC5sM/

problem B

题意:在n个点中连点成线,找到平行的组合最多的情况。

题解:对于点i,枚举所有可能的情况,即找未被选择的点,与i配对。

令x0=横坐标减横坐标,y0=纵坐标减纵坐标,特判一下x00和y00的情况。

1.当x00时,y0inf;

2.当y00时,x0inf;

3.若并非以上两种情况,那就是斜率存在时,对x0,y0求一次__gcd,然后化简x0,y0。

当前ans=ans+mp[make_pair(x0,y0)],再mp[make_pair(x0,y0)]++。 对于ans再求一个最大值就好。

踩坑:x0<0和y0<0的情况;

处理:对于所有x0<0的情况:x0=-x0,y0=-y0。

problem C

题意:有n个人做检查,每个人做每一项检查的时间都是h[i],问你t时刻,每个人在进行或等待着第几项项目。

题解:第一个人直接是t/h[1]+1,后面的人做第一项检查的起始时间是sum[i](h[i]的前缀和),如果t小于sum[i],直接输出1;否则,输出(t-sum[i])/mx+2,mx是他包括他前面的人的h[i]的最大值,+2有1是第一项,还有1是如果刚好整除,就是进入下一项,如果没有整除,就是正在进行下一项。

代码:https://pastebin.ubuntu.com/p/dypwcGCpHF/

problem I

题意:火车公司有两种方案给火车安排座位数量,方案一是让乘客尽可能自由选择,方案二的由火车公司给乘客安排座位。

题解:方案二就是要你求单点最大区间覆盖数,差分一下就ok。方案一是最多区间相交数,要求某区间一共有多少区间和他相交,用n-右端点比他左端点小的区间数-左端点比他右端点大的区间数。右端点比他左端点小的区间数就是求个右端点的前缀和,左端点比他右端点大的区间数就是求个左端点的后缀和。

代码:https://pastebin.ubuntu.com/p/TWM7MT4K3y/

2018-2019 ACM-ICPC Southeastern European Regional Programming Contest

problem B

题意:给你时针、分针、秒针的长度,钟分成n份,要你把三根指针分别指向不同的刻度上,问你一共能形成多少个不同的经过时钟中心的三角形,对2^64取模。

题解:对2^64取模,我们只要用ull自然溢出就好了。我们固定1号针,枚举2号针的位置,找3号针能够放在哪里位置,且2号针只枚举n/2种情况,另一半直接乘2就好。3号针能取到的位置只能在1、2号针对时钟中心的反向延长线所围成的扇形区域内。分n奇偶讨论:n为奇时,cnt=1+2+3+……n/2;n为偶时,cnt=2+3+……n/2,当1、2号针反向共线时,3号针能取除1、2号针的其他位置,所以cnt还要加上n-2。1号针的位置一共有n种情况,所以一共有n2cnt。当三根针有2根相同时,要除以3,三根都相同时要除以6,这时候我们应该先除再乘n。

代码:https://pastebin.ubuntu.com/p/XqcftsR4fY/

problem E

题意:有n条鱼,m个渔夫,每个渔夫的钓鱼竿长为l,给出n条鱼的xy坐标,渔夫只会在(x,0)位置,再给出渔夫的x坐标,渔夫能钓到的鱼只有当鱼坐标(a,b),渔夫坐标(x,0)满足|a-x|+b<=l,问你每个渔夫最多能钓到多少条鱼。

题解:当y[i]>l的时候,没有渔夫能钓到它。否则对于一条鱼(x,y),能钓到它的渔夫的坐标取值为[x[i]+y[i]-l,x[i]-y[i]+l]。我们可以先给渔夫的坐标由小到大排序,然后对每条鱼,算出渔夫坐标取值范围,在渔夫数组里二分查找[l,r],l值是lower_bound,r值是upper_bound,然后差分。因为要按渔夫输入顺序来输出答案,所以一开始渔夫坐标a[i]先copy一份,用map存排序后每个a[i]的位置,最后按顺序输出sum[pos[b[i]]。

代码:https://pastebin.ubuntu.com/p/GgNNGtV2gw/

problem C

题意:有n个点,其中有些点是白点,有些点是黑点,问你能不能找至少m个黑点,使得它们之间最远距离最小。

题解:由于数据范围很小,100,所以我们可以暴力。先用floyd预处理出每两个点之间的距离,然后枚举每两个黑点的距离dis[i][j],把那个距离看成最远距离,看是否能有至少m个黑点它们之间的距离小于dis[i][j],如果有,就更新ans。

代码:https://pastebin.ubuntu.com/p/QbYmQFvtQr/

2018 ICPC East-Central NA Regional Contest

problem A

题意:给出一个n*m的矩阵,表示一个修理厂的停车场。

其中一辆车占两个格子,矩阵中正数a表示a号车,通过它是横着连续两个相同的数还是竖着的可以看出该车的摆放方向。

此外,存在-2格子表示障碍物,存在至少一个-1格子表示什么都没有的空位。

这时,给你一个坐标,表示在这个位置有一个掉落的物品。

问:是否能通过车移动来使物品的位置腾出个空位。(注意车只能在有空位的情况下向停放的方向前后移动,类似推箱子的游戏)

题解:从物品的位置BFS找看看能否找到-1格子,并记下路径,能的话输出路径。

要注意的是:只能沿着车方向走,且每一步向该方向走两格。

AC代码中路径的储存先用了队列同步存下当前移动的车子号码,因为是逆向搜的,所以最后又用了栈输出。(感觉有点点蠢。。找到更好的方法时更新!)

代码:https://pastebin.ubuntu.com/p/fZnyGsqHvN/

problem B

题目给出r,m

要求使数组

A[i]=A[i-1]+d (i>1),A[1]等于r。

对于当前组成第i项的d:

⒈不可以同前i-1项相同,

⒉不可以同前面任意两项形成的差值相等。

然后对于满足以上两个条件的d值,取最小值,然后就可以开开心心组成A[i]啦。

任务:

找到与m有关的i,即满足

①A[i]-A[j]=m(j小于i),

或者

②A[i]=m的情况。

题解:

㈠判断当前i是否为答案

对于当前A[i]:

我们可以给每一个已经确定了的A值开一个数组memb,如果A[k]出现了,就memb[A[k]]=1。

然后查询条件①时就可以直接看memb[A[i]-m]是否为1,是的话,当前i即为答案并结束程序。

对于条件②就直接判断就好了。

㈡找d

开一个数组dist,对于当前i,由dist数组记录出现过的A[i]以及i前面所有A成员与A[i]的差值。出现过就置1。

找最小d,就从令poi=d+1开始,一找到dist[poi]==0的,就可以break了,最小d就是那个poi。

踩的坑:判断是否出现过某个值的时候,用了map的count导致很久才出答案

>>>>>直接数组就好啦?

problem C

题意:有n个候选人,m种投票顺序,每种投票顺序前都有一个p,表示有p个人选择了这种投票顺序,按照给定的pk顺序进行pk,第一轮是第一二个人pk,赢了的人去和第三个人pk,以此类推。在最后一轮获胜的候选人成功当选。问对于每个候选人,是否存在一种pk顺序,使得他必赢。

题解:对于每个人,和能被他打败的人之间连边,然后对于每个人都dfs一次,看能否以他作为起点,遍历所有人。如果可以,就说明存在这样一种方案,否则,则不存在。

代码:https://pastebin.ubuntu.com/p/CPdpyhKgrC/

2018 ACM-ICPC North Central North America Regional Contest

链接 https://cn.vjudge.net/contest/319804 密码:NCNARC2018

problem E

题意:欧拉数:e = sigma(1/i!)(0<=i<=n),给出n,要你求欧拉数。

题解:pre *= i,ans+=pre,要求是精确到12位小数,输出13位就好了。

problem C

题意:给你一个循环小数和循环节的长度,要你给出最简的分数。

题解:混循环小数的分子是不循环部分与第一个循环节连成的数字组成的数,减去不循环部分数字组成的数之差;分母的头几位数字是9,末几位数字是0,9的个数跟循环节的数位相同,0的个数跟不循环部分的数位相同。要求最简分数,最后把分子分母除个gcd就好。PS:纯循环小数就是循环节/n个9(n为循环节长度)。

problem F

题意:给你若干个数的x、y坐标,问斜率绝对值最大是多少。

题解:按x坐标从小到大排序,斜率绝对值最大只能是和相邻的点组成的直线的斜率。所以每个点都和他后一个点算一个斜率,取max就好了。

problem G

题目给出N个点,M条边,以及k1还有k2。对于每条边,有红色(c=1)、蓝色(c=2)、白色(c=0)之分,走过每条边将用去一定的时间。要求从起点s到终点t,找到满足经过k1条红边、k2条蓝边、任意条白边、然后耗时最少的那一次的时间并输出。

用一个优先队列存所有情况,并按用时少的排序。从当前情况出发,找到所有的可达的点,如果连上的边是红边且当前情况的红边数量小于k1的话,那么可以更新(时间,红边)并入队,白边、蓝边处理亦然。

problem I

羊和狼和菜的故事

有个人要运w只狼,s只羊,c只菜去对岸。但是船只能载k个东西(不含人),且在人不在场的情况下,狼吃羊,羊吃菜。 询问是否有一种安排可使得不少狼不少羊不少菜。

狼和菜是不互吃的,所以其实可以把他们看成一种来讨论。 接下来用a代替羊,b代替狼和菜。

①a小于k,可以。因为在有人的时候,是不互吃的,所以人可以每次都带着全部的a,剩余空间用来运b,直到全部运完。

②b小于k同上。

③a等于k的时候,b必须小于等于2k才行。因为先把全部a运到对面后,人再去运k个b过去对岸交换k个a,然后再回去用k个a与剩下的所有b交换(如果b大于2k的话,这时的船没法把余下的b全运走,那么势必会ab互吃),把b运到对岸,最后再回去运a。

④b等于k且a小于等于2k同上。

⑤其他情况都不可以

problem B

最大m子段和模板题。

抄模板过的。记得学习(^U^)ノ~

problem J

题目给出a,b,k三个数,要求求出大小在[a,b]区间的满足“在2到k进制均为回文串”条件的数字数量。

直接暴力枚举,对与[a,b]之间的每个数字都各转换成2~k进制的数组,判断是否为回文串,若不是则下个数字继续从2进制开始判断。

2016 ICPC Mid-Central USA Region

比赛链接:https://www.jisuanke.com/contest/3106?view=challenges

problem B

题意:给你一个2D的图,‘a’代表苹果,’#‘代表障碍物,’.'代表空地,苹果会掉落到空地,遇到障碍物就停,问最后的图会是怎样的。

题解:从下往上扫每一列,用队列存每个空地的坐标。遇到苹果,就把队头取出来,队头的坐标变成’a’,当前坐标变成’.’。遇到障碍物,就清空当前队列。

problem A

题意:给你一个字符,代表下面给的字符串中出现的最大的字符,再给你n个字符串,这n个字符串的给出顺序是按照某种制定的字典序排的,问你能不能给出这种制定的字典序,如果不能,就IMPOSSIBLE,如果有多种可能的字典序,就AMBIGUOUS。

题解:我们队用了超复杂的方法。。。先按给出的字符串顺序建图,(u,v)表示u的字典序大于v的。首先什么时候是存在矛盾呢,就是有环的时候。什么时候是多解呢,就是bfs的时候同一层有多种方案。否则就按bfs时层数最低的先输出。

简单方法:拓扑排序,没能拓扑排序的就是IMPOSSIBLE,bfs时同一层有超过1个点的入度为0就是AMBIGUOUS,否则就按拓扑排序输出。

problem C

题目给出三个矩形的长度与宽度,询问能否组成一个正方形。

给出六条边 假设a和A是一个矩形的两条边,b,B,c,C亦然。 若可以形成正方形,那要么

①ABC且a+b+c==A

要么

②Ab+c且BC且a+B==A

写一个check(A,B,C,a,b,c)函数检查当前一下就好了

对于①很单调,很显然在①中的大写的边肯定比小写的边长。 在②中就不一定了,懒得讨论,直接全部check一下,有三个矩形,每个矩形两条边,这样②中就得check8(222)次:

ABCabc ABcabC AbCaBc AbcaBC aBCAbc aBcAbC abCABc abcABC

problem G

题意:给出一个数字n,接着后面n个数字就是一个序列,这些序列是某个多项式1到n的结果,如式子是3x+3,n=5,序列为3 6 9 12 15。

若这个序列两两相邻的数有不同,则将相邻两个数之差做一个新的序列,直到这个序列所有数都相同。

题目要求输出:1.要生成多少次新序列才能得到所有数相同的序列(我是这么理解的,同时也发现这个数其实就是这个式子的最大次数)

2.原序列下一个数即(n+1)的结果是啥。

题解:由题目给出的图其实就可以看出怎么做了。

检查序列所有数是不是都相同,不同就遍历做新序列,一边计数,反正数量很少,最多就10个数,找到后就能得出第一个结果了。

然后在这个过程中,从原序列开始直到最后得到全部相同的那个序列,每个序列最后一个数加起来总和就是第二个结果了。

代码:https://pastebin.ubuntu.com/p/nM5sz4WYGD/

problem I

题意:在x轴上有n个邮递站,每个邮递站都需要某数量的信件。

0坐标是一个补充信件的地方。邮车从这个地方出发,并能在这里最多补充数量k个信件。

现在要求给每个邮递站其所需要信件的最短路程。

题解:首先因为负轴和正轴都有邮递站,两个因为坐标大小排序处理不一样要分开处理,至于先处理哪边都无所谓,反正0点就能补充信件。

每一边都先给最远的送,用n/k可以看至少要送多少波,注意还要n%k看看是否要多送一波。

若有,在这个多送的一波中就将(k-n%k)的余量给次远的送去,(P.S.如果次远的能在这个过程送完就继续送次次波……这些都能免去路程)如此类推,最后将每次的路程x总和加起来再*2(因为是来回)就能得到答案了。

代码:https://pastebin.ubuntu.com/p/gRGyRzRhx9/

2018 CCPC吉林赛区

problem A

题意:求 {\displaystyle \sum _{i=1}^{n}\lfloor {\frac {n}{i}}\rfloor } {\displaystyle \sum _{i=1}^{n}\lfloor {\frac {n}{i}}\rfloor }(n1e9)的奇偶。

题解:要知道奇偶,那肯定要先知道sum。求sum用O(n)显然超时,这时候整除分块闪亮登场。对于每一个 {\displaystyle \lfloor {\frac {n}{i}}\rfloor } {\displaystyle \lfloor {\frac {n}{i}}\rfloor },有许多 {\displaystyle \lfloor {\frac {n}{i}}\rfloor } {\displaystyle \lfloor {\frac {n}{i}}\rfloor }的值都一样,而且它们呈块状分布。对于每一个值相同的块,最后的一个数是res = n/(n/i)。这个区间的长度就是(res-i+1),这区间里每个点的值都是n/i。然后下一次枚举从res+1开始就好了。

代码:https://pastebin.ubuntu.com/p/BCMzsqKjWh/

problem B

题意:给你四个地方相比于UTC时间差多少,给你某个地方的当地时间,要你求另一个地方的当地时间。答案包含昨、今、明,时间,AM/PM。

题解:由于时间只会在0<=h<=12范围内,所以凌晨12时是12am,中午12时是12pm,且今天的凌晨算today,过完11:59pm的凌晨算tomorrow。先把时间都转成24小时进制,然后按照给出的表±时间,处理好12时的各种情况就好了。

代码:https://pastebin.ubuntu.com/p/XvY6wSF2jc/

problem C

题意:给你n个k值,表示它的值是1/ {\displaystyle 2^{k}} {\displaystyle 2^{k}},问你能不能把这些数分成两组,使得每一组的和都不小于1/2。如果有,请输出任意一组方案,用01代表两个不同的组。

题解:每一组的和都不小于1/2,表明两个组都分别要找一个“1”,而一个“1”等于两个“2”,等于四个“3”……。用cnt1表示第一组要找当前pre的数多少个,用cnt2表示第二组要找当前pre的数多少个。先把每个k值由小到大排序,当pre的值和现在遍历到的值不一样,就cnt1、cnt2乘2,pre++,直至一样。当cnt1+cnt2<=剩下的数,就是NO。如果cnt1不为0,就cnt1–,vis数组记录当前这个数属于第一组。否则就cnt2–。如果最后cnt1和cnt2至少有一个不为0,也是NO的情况。如果是YES,就按照vis数组中记录的情况输出就好了。

代码:https://pastebin.ubuntu.com/p/9Z2YFgDGNS/

problem D

题意:一开始获得奖品的概率q=2%,给你每场游戏获胜的概率,如果赢了,没有获得奖品,q = min(100%,q+2%),如果输了,q = min(100%,q+1.5%)。问你获得奖品的期望步数。

题解:dp[i]表示获得奖品为q时的步数,因为有1.5%,所以所有数都扩大两倍。对于dp[i] = 1.0 + 本场获得胜利但没有赢奖品的后续步数 + 本场输了的后续步数。用记忆化搜索,当q>=200的时候,q=200,return 1/P。初始时dp[i]都等于-1,当dp[i]大于-1时直接return。答案是dp[4]。

代码:https://pastebin.ubuntu.com/p/VpRZ7mcyQp/

2018 Rocky Mountain Regional Contest

网址:https://cn.vjudge.net/contest/320635 密码:RMRC2018

problem A

签到题,直接把两个数相乘然后累加。

problem G

题意:有数轴上有n个点,有m个点是绿色的,其余的点是红色的,给你m个绿色点的位置,问从某一个点出发,去到另一个点(可以到自己),包含至少1个绿色点的方案一共有多少种。(正反方向只计算一次)

题解:因为给出的绿色的点的位置是从小到大给出的,所以直接用一个变量j记录在自己正方向离自己最近的绿色点的坐标pos,然后就ans+=n-pos+1。

problem D

题意:给出一个长度为n的序列,求一个最大数字H满足条件——序列中有至少H个数字大于或等于H。

题解:排序后从大到小开始计数。当cnt>a[i]时break得出答案。

problem J

题目给出一张图,边有7种颜色,每条边有一定的通过时间,问从点1出发,回到点1且经过了7种颜色的路花费的最小时间。

在bfs基础上改动一下就好了

存状态的队列用优先队列,把状态中时间小的先pop出来

vist数组变成8维的,比如vist[i][a][b][c][d][e][f][g],a 、b 、c 、d 、e 、f 、g代表七种颜色条件满足状态,要么0要么1,代表这种颜色没出现过、出现过,其中i表示时间。

接下来就跟bfs差不多了,对于每个当前状态,把所有可能的状态枚举并加入优先队列。

终止状态,第一个到达点1且各种颜色数量大于等于1的状态,输出时间并return 0。

problem C

在一个坐标系中,原点O站着我,点(x,y)站着一个人,简称叫A吧。然后在坐标系中,每一个整数坐标都是一颗树。现在又在给出一个矩形,左下角坐标为(xa,ya),右上角为(xb,yb),将这个矩形里的树包括其边界上的树都砍掉,询问此时我能不能看到A,可以的话输出Yes,不可以输出No以及我在看向A时看到的那棵树(第一棵阻挡我看A的视线的树)的坐标。

纸上画一下,就大概知道求原点到某个点之间的第一个整数坐标,其实就是gcd一次,然后接下来OA线段上所有的整数坐标也可以表示出来了。

设k为gcd(x0,y0),那么在OA上将有k-1个整数坐标,也即阻挡我的视线的树有k-1棵。

设第一个整数坐标为(x1,y1),

其中x1=x0/k,y1=y0/k

那个相应的第二个坐标将是(x2,y2),

x2=x1+x0/k,y2=y1+y0/k

就这样推下去就行了。 好了,然后题目问能否看到A,能看到A的情况有

①k=1(无遮挡)

②k=2(只有一棵树),且那棵树在矩形内

③k>2且视线中第一个整数坐标和最后一个整数坐标都在矩形内部

否则就是No

判断整数坐标(x,y)是否在矩形内部

xa<=x且ya<=y且xb>=x且yb>=y

problem E

给出m个车道,每个车道都有n个直道以及n-1个弯道,每个车道的第i个直道长度都为d[i],第x车道的第i个弯道长度为s[i]+c[i]*x。 直道弯道将按下面分布

直-弯-直-弯-直…-弯-直

然后在直道(a)上变道到隔壁道(b)上时,需要花费k+r距离,且假设变道前在a上横坐标为t,变道后在b上横坐标为t+k。 即在第p个直道上,从i道变道到j道,需要满足

k*(abs(i-j))<=d[p],

然后走完该直道将花费d[p]+r*(abs(i-j))。 也就是说,变道1次,在这个直道上比不变道多走了r。

题目问,从第1道出发,回到终点时也必须是第1道将花费的最小时间。

优先队列存状态,包括时间已经当前已走过的直道数zhi和弯道数wan。优先pop出状态时间小的出来讨论。 对于每一个状态,推出后继状态, 在直道上只要满足变道条件,就变道并从该车道走过弯道,计算时间后,记得更新zhi和wan,进队。不变道状态的也要进队。

当zhiwann-1时,就只要考虑直道,并更新zhi=n,而且不是第1道的都变道到第一道(前提是满足变道条件)。把可以变道到第1道的情况加进队列,不需变道的也要加进队列。 最后如果当前zhin且wann-1时,输出时间并return 0。

problem H

题意:你有M台机器,每台机器都可以处理1秒的计算或者处理Q秒的计算。

现在给出Q——除1秒外另一方案的秒数

M——你拥有的机器数量

S——客人需要的处理1秒的订单数量

L——客人需要的处理Q秒的订单数量

每台机器只能同时花相应时间处理一个订单。

现在要求求出处理完订单的最短时间。

题解:先每台机器平均分配处理完Q秒的订单。如果Q秒的订单数量刚好能让每台机器被平均分配完,那就继续平均分配1秒的订单。不能平均分配完就在平均分配完的基础上+1秒。

Q秒的订单不能平均分配完的话,就表示在那些空的机器每个都能塞进去Q个1秒订单。因为多出来的Q秒必须要用的~这样就能省时间了。塞完后还有1秒的订单的话就平均分配了就OK了

2019牛客暑期多校(第九场)

problem D

题意:给n个数,和sum,要你选其中一些数,使得它们的和等于sum。(n最大为36)

题解:状压+meet in the middle。

代码:https://pastebin.ubuntu.com/p/vj9x9JpzyM/

problem E

题意:有n个点,每次增加一条边,问每一次共有多少种选4个点的方案,使得它们两两之间不连通。

题解:每一次合并的时候,ans减去不合法的答案。ans一开始等于C(n,4)。每一次加边,如果它们之间是不连通的,不合法的方案数就等于这两个集合的大小的乘积再乘上从其他集合中选两个不在同一集合的方案数。从其他集合中选两个不在同一集合中的方案数=(其他集合的大小的和的平方-平方的和)/2。用一个变量维护平方的和。

代码:https://pastebin.ubuntu.com/p/Dq4WYddyJY/

2019牛客暑期多校(第十场)

比赛:https://ac.nowcoder.com/acm/contest/890#question

problem J

题意:给你5种同分异构体的分子模型,再给你n种他们的连接方式,判断属于哪种同分异构体

题解:可以发现除了第二三种他们每个点的度都一样,其他的都可以用度来区分。第一种有4个度为2的点,第四种有4个度为1的点,第五种有1个度为4的点。第二第三种可以用度为3的那个点连着的三个点的度来区分,第二种度为3的点连着两个度为1的点和一个度为2的点,第三种度为3的点连着两个度为2的点和一个度为1的点。

代码:https://pastebin.ubuntu.com/p/wypRNpY334/

problem E

题目定义了一种排序: 在长、宽为2的i次方的矩阵中,把矩阵分成4个部分并标号

1 | 4

2 | 3

此时一个长、宽为2的(i-1)次方的矩形,假设其排列轨迹为A;那么上面的矩形可由下面的矩形组成: 那么各个部分的排列:

1:A逆时针90度后再上下翻转

2:A

3:A

4:A逆时针90度后左右翻转

(以上只考虑排列轨迹不考虑坐标值)

现在题目给出n,k,并给出n给点的坐标,k表示该矩阵是(2(k))*(2(k))的规模。 求按定义的排序后,n个点的顺序。

对于每一个矩形,把他分成4份,找出在长宽为2(i)的矩形“各部分”的坐标值和在长宽为2(i-1)的矩形的坐标之间的联系。 可以发现

假设长宽为2^(i-1)的某一点坐标为(x0,y0);

则在规模为2^(i)的矩形中,各部分的点在该部分中的排序与(x0,y0)存在下面关系:

1:(x0,y0)==(y,x); //部分1中的点(x,y)变成(y,x)后,在部分1中与(x0,y0)的排序相等

2:(x0,y0)==(x-m,y);

3:(x0,y0)==(x-m,y-m);

4:(x0,y0)==(2*m+1-y,m+1-x)。

(考虑坐标情况下的对应)

令m为矩形长度的一半,则:

各部分容量为m*m. 部分1排在部分2前,2排在3前,3排在4前;

所以每个点的排序=[(部分j-1)mm]+[对应的点(x0,y0)的排序]

因为数据的问题,所以对于每一个点,先查询其在矩形中的排序,然后再根据这个排序的值,进行排序并输出。 用递归: 对于当前坐标点为(x,y),矩形规模为2^(d)的时候:

1)判断在哪个部分

2)根据所在部分进行坐标转换并更新临时答案

3)return条件为当d==0时,返回1.

//一直想要正向地把全部点的排序算出来并储存,但考虑到空间限制做不了。。没反应过来可以直接逆向查每个点的排序

//应当多考虑数据范围的限制并从小的下手

代码:https://paste.ubuntu.com/p/7bKNdXdjWN/

problem B

题意:定义S(1)=“COFFEE”,S(2)=“CHICKEN”,S(n) = S(n-1)+S(n-2),即S(3)=“COFFEECHICKEN”,给你n和k,要你输出S(n)第k个字符开始,往后10个字符,如果不够10个,就输到S(n)的结尾。

题解:用f(i)存S(i)的长度。solve(n,k)表示S(n)第k个字符,如果n<=2,直接return结果。否则,如果k>=f[n-2],就return(n-1,k-f[n-2]),否则return(n-2,k)

代码:https://pastebin.ubuntu.com/p/9ZZg7QR2Nd/

problem D

经典韩信点兵题。

用中国剩余定理扩展。

此题数据很大,要用java大数过。(但是也有用c++过的

problem F

题意:水平、竖直方向各打三枪,每打一枪,该坐标该方向上的所有气球都会爆,但同一个方向不同枪要相隔r个单位,问最多可以射爆多少个气球。

题解:枚举打哪三行,然后用线段树求出打这三行的时候,竖直方向上最多射爆多少个气球。相加就能更新答案。线段树维护[l,r]区间内选三列气球来打,最多打多少个。

代码:https://pastebin.ubuntu.com/p/yrzkmXkYx6/

你可能感兴趣的:(acm)