2015 CQU 重庆大学程序设计竞赛 解题报告



前言

儿童节快乐~~ 啊对了首先想带标程回家看的可以来这里: 教主大大标程包 / 我的现场赛代码

在校赛这样三人组队两台电脑的环境下,单挑的压力着实是十分之大……毕竟在同等条件下别人手速只要超过自己的一半就一定会在罚时上输掉。

所以只有尽可能的减少WA的次数,比赛结束时是 8*Submission/6*AC 的 W/A 率,对自己而言确实是个难得的正确率了。

作为ATeam1,总觉得啊又是A又是1的,不努努力怎么对得起我的对标,然则全场拼的话又拼不过手速,只有速读速写了,

最终A题抢一血提交,却在每组测试数据前忘了给balance清零,于是抢到了全场第一发WA(第一个WA应该叫啥,FW么)快哭了,然后再提交的时候已经是ID4了,即使AC也不是FB了……

然后奋力过B,C,都是一次过,这两题中有一题我是FB,可惜忘了是哪题了……

然后去做了F,很可惜没看到题目中有一句“Tb>Ta”,即同时发送信息是不可以的,又送了一发WA,第二发才AC掉……

看到D那么多那么多条件,当场吓傻了……这……这还让不让人好好做题啦!

等会……教主不是最喜欢把签到题隐藏在看起来最烦的题里么,仔细读了读题发现,啊可食用商品直接乘以0.8来考虑嘛,然后删掉了一群句子,然后就是那个买了不可食用物品的话则所有物品中价格最低的那个打五折……刚打算动手写个分支(价格最低的是不可食用/可食用),突然反应过来啊不对啊,无论这个人接下来买啥,肯定不会比只买这个非食用物品的折扣低(买了价值高的,因为价值比这个非食用物品高,不考虑,买了价值低的,折扣变为这个价值低的物品的一半),突然这道题就变成裸贪心了……哗啦哗啦写掉1A

然后手头剩下一个G和一个E,看到E我就好难过……啊啊啊教练我要学图论……

只有啃G了,……然而G题也是个题目一时半会不知道是啥的题目(每道题第一时间一定是被教主又在玩什么ACG梗或者电影梗吸引),然后在纸上随手画了画……啊咧,这个画法似曾相识……啊,凸包么!能用三角形框起来的意思就是说并没有点在最终的三个点框成的三角形之外,这不就是凸包嘛~ 然后敲了个凸包,凸包点数不大于三即可,Submit~ 

然后E……我瞪着它它瞪着我……除了枚举然后dfs放守卫以外我想不出任何解法……然而写着写着… 惊讶地发现……啊为何我本来只想写个dist数组来存,莫名其妙写成dp了……(刚刚打字打dp居然打成“大破”了我也是捂脸……) 然后就想干脆改成dp吧…… 然后又觉得唔还是写个递归从鹰眼开始来获得每个节点的各个分支需要多少个警卫求和吧……然后……啊对哦递归深度怎么可能到1K……

Time‘s up


解题报告

Problem_[A]kali’s Balance

timelimit 2 seconds / memory limit 256 megabytes

 

Description

艾欧尼亚群岛上存在一个上古教派,致力于维护某种均衡。规则与混乱、光明与黑暗——万物必须和谐共存,这就是宇宙的真理。该教派名为均衡教派,维护世界均衡的神圣使命则由三个暗影战士来执行。阿卡丽便是这三名暗影勇士中一员,她肩负着修枝的神圣职责——将威胁瓦洛兰大陆和谐的敌人消灭干净。

阿卡丽自小和母亲一起习武,练就一身好武功。母亲的训练严酷无情,她的基本原则是:“我们是在替天行道”。阿卡丽十四岁加入均衡教派,那时她就能空手砍断链条。毋庸置疑,她将继承母亲“暗影之拳”的名号。她的所作所为别人可能无法理解,但在阿卡丽看来,她在执行母亲神圣的信条。她要和慎和凯南一起维护着瓦洛兰大陆的平衡。这一神圣的追求毫无疑问地将三个暗影忍者引领向正义之地。

作为一个捍卫均衡的忍者,她很早以前就被师傅嘱托需要监视另一块神秘的大陆HUXI:有许许多多不同的神秘能量散布在这片大陆的各个角落,每份神秘能量都可以以一个正整数来描述其蕴含的能量值。年轻的暗影忍者认为这块神秘的大陆上的能量值为奇数的神秘能量数量若与能量值为偶数的神秘能量数量保持严格一致,那么这片大陆便是平衡的。

 

Input

第一行一个正正数T(T<=20),描述测试数据组数。

接下来每两行描述一组测试数据:

每组测试数据的第一行含有一个整数N(1<=N<=1024)描述神秘大陆HUXI上的神秘能量数量;

每组测试数据的第二行含有N个正整数Ai(1<=Ai<=1024)描述这些神秘能量所蕴含的能量值。

 

Output

对于每组测试数据,依次输出一个大写英文字母’B’表示神秘大陆HUXI是平衡的,或输出一个大写英文字母’U’表示大陆HUXI不平衡;

每组测试数据的输出独占一行(即每个大写英文之母之后需要换行,详如Sample)。

 

Sample Input:

Sample Output:

2

4

1 2 3 1024

4

1 2 3 3

B

U

 

梗:英雄联盟-均衡教派-阿卡丽

题意:给n个数,奇数个数等于偶数个数的时候输出B(Balance),否则输出U

思路:均衡,存乎万物之间!用一个均衡值(不好,我的中二之魂!)来代表均衡的程度,初始为0,出现一个奇数减一,出现一个偶数加一,最终如果均衡值依然为0则输出B,否则输出U。

Code:

#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

int main()
{
    freopen("A.in","r",stdin);
    int T; scanf("%d",&T);
    int a[1111];
    for(int _T=0;_T<T;_T++)
    {
        int balance=0;
        memset(a,0,sizeof a);
        int n;  scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]%2==1) balance++;
            else balance--;
        }
        if(balance==0) printf("B\n");
        else printf("U\n");
    }
    return 0;
}

 

Problem_[B]rain Burst

timelimit 2 seconds / memory limit 256 megabytes

 

Description

Brain Burst 2039是日本轻小说作家川原砾创作的轻小说作品《加速世界》中的一款游戏的名称。

这是一款VR-MMORPG(Virtual Reality - Massively Multiplayer OnlineRole-playing game)虚拟现实大型多人在线角色扮演类游戏,这款游戏与普通的MMORPG大型多人在线角色扮演类游戏有一个很重要的相似点:等级非常重要。

玩家刚进入游戏时等级为Lv.1,当前经验值为0 exp, 当前等级升级需要经验值为300 exp。玩家在游戏中获取经验将会累加在自己的当前经验值上,若一位Lv 1玩家获取经验后,一旦当前经验值不少于300 exp时便会升级,这时系统会立即在玩家的当前经验值上扣除玩家升级所需经验的,然后将玩家的等级提升至Lv 2。

当然,升级所需经验会随着玩家等级的提升不断的升高,若当前玩家等级为K(K>=1),则玩家当前等级升级需要经验值为300+(K-1)*100。

然而在某些特定条件下,玩家甚至会连续升级:例如一名当前等级为Lv 2, 当前经验值为 200 exp的玩家获取1000 exp经验后,那么他将会升级到Lv 4, 当前经验值剩余300 exp (升级到Lv 3后当前经验剩余800,不低于Lv 3到Lv 4的升级所需经验500故连续升级)。

由于游戏开发需要, 现在给定某玩家获取N exp经验后的等级Lv. L,与该玩家获取经验后的当前经验值E exp,需要编程计算求得这位玩家在获取这N exp经验之前的等级和经验值。

 

Input

第一行一个正正数T(T<=100),描述测试数据组数。

接下来每行描述一组测试数据:

每组测试数据唯一一行含有3个非负整数,依次为N(0<=N<=10000)玩家获取经验,L(1< =L<=99)玩家获取经验后等级,E(0<=E<=300+(L-1)*100)玩家获取经验后当前经验值。输入数据保证合法性(即玩家在获得经验前至少为Lv1, 0 exp)。

 

Output

对于每组测试数据,依次输两个非负整数分别表示玩家在获取经验前的等级和经验值。

每组测试数据的输出独占一行(即每两个非负整数之后需要换行,详如Sample)。

 

Sample Input:

Sample Output:

5

1000 4 300

300 2 0

100 1 200

0 4 300

1985 6 100

2 200

1 0

1 100

4 300

2 315

 

梗:ACG-动漫-加速世界 / ACG-MMO-等级制MMO

题意:正所谓在MMO中高等级玩家单手干掉一群神装萌新,等级很重要。这题的意思是,每行给三个数N / L / E,即我获得了N点经验值,现在是L等级多E点经验值,问我获得这N点经验值之前我是多少级多少经验值呢?

思路:等级+经验的表述一般是表象,实际上仍然是总经验(升级到各等级所需经验和+多余经验)这样的一个值,我们事先计算一下升到各个等级需要的总经验值存在exp数组里,那么每次给出的L和E我们都可以通过exp[L]+E的方式来表示总经验,减去N之后就是所需要的之前的经验值。再在数组里进行查找(这个数据范围是100,遍历和二分查找其实差别不大,不过大家还是养成二分的好习惯哦,如果这是1e9的等级数的对不对~~ 啊不过那么高等级谁还愿意玩啊……),查找到之后等级就知道了,然后就能减掉它获得剩余经验啦~

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

int exp[101];

void exp_init()
{
    memset(exp,0,sizeof exp);
    exp[0]=-1;
    exp[1]=0;
    for(int i=2;i<101;i++)
    {
        exp[i]=exp[i-1]+300+(i-2)*100;
        //cout<<i<<":"<<exp[i]<<endl;
    }
}

int main()
{
    freopen("B.in","r",stdin);
    int T;  scanf("%d",&T);
    exp_init();
    for(int _T=0;_T<T;_T++)
    {
        int n,l,e;
        scanf("%d%d%d",&n,&l,&e);
        int now=exp[l]+e-n;
        int lv= upper_bound(exp,exp+101,now)-exp-1;
        int rest=now-exp[lv];
        cout<<lv<<" "<<rest<<endl;
    }
    return 0;
}

这里需要说明的是,upper_bound()函数是STL中自带的“有序数列中二分查找到第一个大于key的位置”函数,这里的用法是在exp[0]到exp[100]寻找now的值所在的位置,减掉exp的首地址就是下标的值了。(这里用for遍历一下找到第一个比now大的-1或者找到最后一个比now小的都是可以的哦)


Problem_[C]ross Mines

timelimit 2 seconds / memory limit 256 megabytes

 

Description

传统扫雷是一款单人的电脑游戏。游戏目标是找出所有没有地雷的方格,完成游戏;要是按了有地雷的方格,游戏失败。游戏以完成时间来评高低。

游戏开始时,玩家可看到一堆整齐排列的空白方块,方块数可由玩家自行选择。如果是第一次点开方块则不会踩到地雷。如果玩家点开没有地雷的方块,会有一个数字显现其上,这个数字代表着邻近方块有多少颗地雷(数字至多为8),玩家须运用逻辑来推断哪些方块含或不含地雷。


交叉扫雷与传统扫雷不同,没有地雷的方块上的统计数字统计的并非相邻方块上的地雷数量,而是统计以自己为正中心的5*5的正方形两条对角线上除自己之外的方格(之后略称为相对交叉位置)的地雷数量,如上图所示。

作为交叉扫雷的开发人员,你需要编程解决在输入所有地雷位置的情况下按照交叉扫雷规则标记所有没有地雷的方块其相对交叉位置方块中的地雷数量。

 

Input

第一行一个正正数T(T<=20),描述测试数据组数。

每组测试数据的第一行含有一个整数N(1<=N<=16)描述交叉扫雷游戏棋盘尺寸为N*N;

每组测试数据的第二行起总计N行每行共N个字符(小写字母‘x’表示该方块含有地雷,小写字母‘o’表示该方块没有地雷)。

 

Output

对于每组测试数据,依次输出一个N*N的字符矩阵对应每组输入数据。矩阵含有小写字母’x’表示对应方格含有地雷或数字’0’-’8’表示对应方格其相对交叉位置方块中的地雷数量。

 

Sample Input:

Sample Output:

2

5

xoxxo

ooxxo

xoooo

xoxxo

ooxoo

3

ooo

ooo

xxo

x1xx1

14xx2

x4324

x3xx1

02x11

001

111

xx0

 

梗:扫雷

题意:想必各位都写过扫雷吧,这里唯一不同的是每个格子里的数字不是周围一圈的雷数而是一个叉上的雷数。让输出这个图(所有非雷格输出数字)

思路:首先我们用int[][]代表每个格子周围一个叉范围内的雷数,用char[][]读入这张图,读入过程中一旦当前读入的是个雷,则开始遍历周围一个叉范围内的所有格子(这里记得范围哦,先判断这个坐标在不在图内再去访问格子的数组,否则会RE数组越界的),对int[][]数组的当前位置进行++,读完了,数字们也就加完了。然后输出这张图,如果char[][]数组中这个地方是雷就输出x,否则输出int[][]中的数字。

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

char mp[20][20];
int nmp[20][20];

void update(int n,int x,int y)
{
    int dir[8][2]={-2,-2,-1,-1,1,1,2,2,1,-1,2,-2,-1,1,-2,2};
    for(int i=0;i<8;i++)
    {
        if(x+dir[i][0]>=1 && x+dir[i][0]<=n && y+dir[i][1]>=1 && y+dir[i][1]<=n)
            nmp[x+dir[i][0]][y+dir[i][1]]++;
    }
}

int main()
{
    freopen("C.in","r",stdin);
    int T;  scanf("%d",&T);
    for(int _T=0;_T<T;_T++)
    {
        memset(nmp,0,sizeof nmp);
        int n=0;    scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            getchar();
            for(int j=1;j<=n;j++)
            {
                scanf("%c",&mp[i][j]);
                if(mp[i][j]=='x') update(n,i,j);
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(mp[i][j]=='x')printf("x");
                else printf("%d",nmp[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

Problem_[D]iscount Shopping

timelimit 2 seconds / memory limit 256 megabytes

 

Description

重庆大学ACM-ICPC代表队共N名队员共同前往ACMOUNTAIN出席比赛。赛前,大家偶然发现位于ACMOUNTAIN的ACMARKET正在进行大促销活动。

ACMARKET的商品均分为可食用和不可食用两种,可食用商品全部8折优惠(原价*0.8),不可食用商品原价。

       若任意一个人购买了至少一件不可食用商品,那么他的所有所购买的商品中实际购买价格(即若是可食用商品将会以8折后价格计算)最低商品中的其中一件(即有多件商品实际购买价格最低时仅能优惠其中一件)会在实际购买价格的基础上再享受一次5折优惠(即若享受8折后的可食用商品是客户所购买的最低价的商品那么他可以选择以原价*0.8*0.5的价格来购买这件商品)。

代表队员们列出了他们希望购买的M件商品,请问N名队员最少共计使用多少钱可以购买该清单上的全部商品(每名队员最多仅能购买一次商品,但一次购买数量不受限制)。

 

Input

第一行一个正正数T(T<=20),描述测试数据组数。

接下来每行描述一组测试数据:

每组测试数据第1行两个整数,依次为N(1<=N<=10000)代表队队员个人, (1<=M<=10000)清单商品个数;

每组测试数据第2至1+M行每行两个整数,描述每一件在清单上的商品,P(1 <=p<=10000)商品原价,T是否为可食用(T=0时该商品为不可食用,反之T=1时该商品为可食用)。

 

Output

对于每组测试数据,输出一个浮点数(保留两位小数,四舍五入)即购买清单上全部商品最少的花费。

每组测试数据的输出独占一行(即每个浮点数之后需要换行,详如Sample)。

 

Sample Input:

Sample Output:

2

2 3

1024 0

1024 1

512 0

2 5

81 1

511 0

2137 0

155 1

1024 0

1587.20

2759.90

 

梗:ACM / ACMer买东西遇到促销一定要找(jing)最(da)优(xi)解(suan)
题意:有n个人要去买m个东西,每个东西有原始价格(p,食用物品本质上是p*0.8)和属性(T,1为可食用,0为不可食用),对于每个人来说,只要他买了一个非食用物品,在他的购物车中所有的物品中最便宜的那个,打五折。问最少花多少钱。

思路:我们呢还是反着看吧,我们最多省多少钱。对于每个人来说,买了非食用物品的话,实际上省钱的最大值为(购买了的各非食用物品价值的最小值与各食用物品最小值 之间较小的一个),唔是不是很绕,这么说吧,买一个非食用之后,无论买啥都不如单单买它一个折扣低(理由见前言),为了省钱最大,我们把非食用排个序,食用排个序,前n-1个人依次购买非食用物品中剩下的最大值,仅买这一个(当非食用物品买完了,那就break掉吧,因为已经不会再有折扣了),最后一个人买完剩下的所有东西(为什么有种强烈的“啊我就是这最后一个人”的既视感),最后一个人的折扣值取决于他的购物车里还有没有非食用物品,以及购物车中最便宜的物品是什么。

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

double fd[10086],nfd[10086];
bool cmp(const double a,const double b)
{
    return a - b > 0.0;
}

int main()
{
    freopen("D.in","r",stdin);
    int T; scanf("%d",&T);
    for(int _T=0;_T<T;_T++)
    {
        int n,m,posf=0,posn=0;
        double sum=0.0;
        scanf("%d%d",&n,&m);
        memset(fd,0,sizeof fd);
        memset(nfd,0,sizeof nfd);
        for(int i=0;i<m;i++)
        {
            int p,t;
            scanf("%d%d",&p,&t);
            if(t==1)
            {
                fd[posf++]=(double)p*0.8;
                sum+=(double)p*0.8;
            }
            else
            {
                nfd[posn++]=(double)p;
                sum+=(double)p;
            }
        }
        sort(fd,fd+posf,cmp);
        sort(nfd,nfd+posn,cmp);
        int lenf=posf,lenn=posn;
        posf=posn=0;
        for(int i=0;i<n;i++)
        {
            if(posn==lenn)break;
            if(i==n-1)
            {
                if(posf==lenf)sum-=nfd[lenn-1]/2.0;
                else sum-=min(nfd[lenn-1],fd[lenf-1])/2.0;
            }
            else
            {
                sum-=(nfd[posn]/2.0);
                posn++;
            }
        }
        printf("%.2f\n",sum);
    }
    return 0;
}

嗯,关于变量名,fd是food,nfd是non-food,lenf是length_of_food,lenn是length_of_non-food,啊,别打我,现场赛想不出好名字……


 

Problem_[E]agle Eye

time limit 2 seconds / memorylimit 256 megabytes

 

Description

鹰眼是美国漫威漫画公司旗下的超级英雄。本名克林顿·法兰西斯·巴顿,小名克林特,曾化名为歌利亚与浪人,是个在马戏团长大的孤儿,师从剑客和捷射,天赋异常,少年时便获得“鹰眼“和“世界最佳狙击手”的称号,因在某次演出时看到钢铁侠救人的一幕后,决心利用自己的能力成为超级英雄,后加入复仇者联盟,成为其中重要的一份子。在复仇者集结之前,鹰眼为神盾局工作。

在九头蛇的猛烈进攻下,鹰眼接到守卫一个神盾局秘密地下基地的任务:

秘密基地中有N个密室,这N个密室间共有N-1条1个单位长度的通道相连,任意两个不同的密室都相互可达(或需要穿过其他的密室)。

神盾局需要鹰眼以及一些神盾局特工呆在部分密室中(不可移动)来保护这全部N个密室。

鹰眼由于其特殊的能力,他能够守卫自己所在的密室以及距离他所在密室不超过K个单位长度距离的所有密室。

其他特工能够守卫自己所在的密室以及距离他所在密室不超过1个单位长度距离的所有密室。

鹰眼希望你帮助他计算出要保护所有N个密室最少需要派遣多少名神盾局特工(鹰眼自身不计算在内)。

 

Input

第一行一个正正数T(T<=100),描述测试数据组数。

接下来每行描述一组测试数据:

每组测试数据第1行2个整数,依次为N(1<=N<=1000)表示密室数量, K(1<K<=5)表示鹰眼守卫范围;

每组测试数据第2至N行每行两个整数X Y,描述一条密室通道,说明密室X与密室Y间存在一条通道(1<=X, Y<=N, X!=Y),保证输入数据满足题意限制。

 

Output

对于每组测试数据,输出一个非负整数即最少派遣特工的数量。

每组测试数据的输出独占一行(即每个非负整数之后需要换行,详如Sample)。

 

Sample Input:

Sample Output:

1

9 2

1 2

2 3

3 4

4 5

3 6

6 7

6 8

8 9

1

 

梗:Marvel-复仇者联盟-鹰眼 / 神盾局特工
题意:有一棵所有通路权值都为1的树,超级英雄鹰眼站在其中一个节点上,然后距离k以内的都被控制住,除此之外,放置有限数量个特工(每个人可以控制距离1以内的节点),为最少需要多少个特工才能把这棵树的所有节点变为可控。据说一种解法是枚举鹰眼的位置然后树形dp,由于这题我并没有做出来……放上教主的标程。
Code: By Jki

#include <cstdio>
#include <cstring>

#define V 11000
#define E 21000
#define S 3

int n, m, ans;
int nbs[V], nxt[E], dst[E], enm;
int f[V][S];

const int inf=10086;

template<class T> inline void updmin(T &x, const T y){ if(x>y)x=y; }
template<class T> inline void updmax(T &x, const T y){ if(x<y)x=y; }

inline void add_edge(const int u, const int v){
	nxt[++enm]=nbs[u]; nbs[u]=enm; dst[enm]=v;
	nxt[++enm]=nbs[v]; nbs[v]=enm; dst[enm]=u;
}

int dp_entry(const int u, const int p, const int d){
	int sum=0;
	for(int i=nbs[u]; i; i=nxt[i]){
		int v=dst[i];
		if(v==p)continue;
		sum+=dp_entry(v, u, d+1);
	}
	f[u][0]=0;
	f[u][1]=0;
	f[u][2]=1;
	int bst=inf;
	for(int i=nbs[u]; i; i=nxt[i]){
		int v=dst[i];
		if(v==p)continue;
		f[u][0]+=f[v][1];
		f[u][1]+=f[v][1];
		updmin(bst, f[v][2]-f[v][1]);
		f[u][2]+=f[v][0];
	}
	f[u][1]+=bst;
	updmin(f[u][1], f[u][2]);
	updmin(f[u][0], f[u][1]);
	if(d<m){
		return sum;
	}else return f[u][0];
}

int main(){
	int T;if(!scanf("%d", &T))return 1;
	while(T--){
		if(scanf("%d%d", &n, &m)!=2)return 1;
		memset(nbs, 0, sizeof(nbs));enm=0;
		for(int i=1; i<n; i++){
			int u, v;
			if(scanf("%d%d", &u, &v)!=2)return 1;
			u--;v--;
			add_edge(u, v);
		}
		ans=inf;
		for(int i=0; i<n; i++){
			updmin(ans, dp_entry(i, -1, 0));
		}
		printf("%d\n", ans);
	}
}

Faster Code: By Jki

#include <cstdio>
#include <cstring>

#define N 11000
#define E 21000
#define M 8
#define B 2

const int INF=10086;

int n, m;
int nbs[N], nxt[E], dst[E], enm;
int f[N][B][B][M], ans;

template<class T> void updmax(T &x, const T y){ if(x<y)x=y; }
template<class T> void updmin(T &x, const T y){ if(x>y)x=y; }

void add_edge(const int u, const int v){
	nxt[++enm]=nbs[u];nbs[u]=enm;dst[enm]=v;
	nxt[++enm]=nbs[v];nbs[v]=enm;dst[enm]=u;
}

void dp_entry(const int u, const int p){
	for(int i=nbs[u]; i; i=nxt[i]){
		int v=dst[i];
		if(v==p)continue;
		dp_entry(v, u);
	}
	/* GUARDED */
		//+EE
		f[u][1][1][m]=0;
		for(int i=nbs[u]; i; i=nxt[i]){
			int v=dst[i];
			if(v==p)continue;
			f[u][1][1][m]+=f[v][0][0][m];
		}
		//1EE
		for(int k=m-1; k>=0; k--){
			updmin(f[u][1][1][k], f[u][1][1][k+1]);
			//0AGENT
			int buf=0, bst=INF;
			for(int i=nbs[u]; i; i=nxt[i]){
				int v=dst[i];
				if(v==p)continue;
				updmin(bst, f[v][1][1][k+1]-f[v][0][0][k]);
				buf+=f[v][0][0][k];
			}
			updmin(f[u][1][1][k], buf+bst);
			if(k==1){
				//+AGENT
				buf=0; bst=INF;
				for(int i=nbs[u]; i; i=nxt[i]){
					int v=dst[i];
					if(v==p)continue;
					updmin(bst, f[v][0][1][1]-f[v][0][0][1]);
					buf+=f[v][0][0][1];
				}
				updmin(f[u][1][1][1], buf+bst+1);
			}else if(k==0){
				int x_fir, x_sec;
				int y_fir, y_sec;
				x_fir=x_sec=y_fir=y_sec=-1;
				buf=0;
				for(int i=nbs[u]; i; i=nxt[i]){
					int v=dst[i];
					if(v==p)continue;
					buf+=f[v][1][0][0];
					if(x_fir<0 || f[x_fir][1][1][0]-f[x_fir][1][0][0]>=f[v][1][1][0]-f[v][1][0][0]){
						x_sec=x_fir; x_fir=v;
					}else if(x_sec<0 || f[x_sec][1][1][0]-f[x_sec][1][0][0]>f[v][1][1][0]-f[v][1][0][0]){
						x_sec=v;
					}
					if(y_fir<0 || f[y_fir][1][0][1]-f[y_fir][1][0][0]>=f[v][1][0][1]-f[v][1][0][0]){
						y_sec=y_fir; y_fir=v;
					}else if(y_sec<0 || f[y_sec][1][0][1]-f[y_sec][1][0][0]>f[v][1][0][1]-f[v][1][0][0]){
						y_sec=v;
					}
				}
				if(~x_fir && ~y_fir){
					if(x_fir!=y_fir){
						updmin(f[u][1][1][0], buf+f[x_fir][1][1][0]-f[x_fir][1][0][0]+f[y_fir][1][0][1]-f[y_fir][1][0][0]);
					}else{
						if(~x_sec){
							updmin(f[u][1][1][0], buf+f[x_sec][1][1][0]-f[x_sec][1][0][0]+f[y_fir][1][0][1]-f[y_fir][1][0][0]);
						}
						if(~y_sec){
							updmin(f[u][1][1][0], buf+f[x_fir][1][1][0]-f[x_fir][1][0][0]+f[y_sec][1][0][1]-f[y_sec][1][0][0]);
						}
					}
				}
			}
		}
		updmin(f[u][0][1][0], f[u][1][1][0]);
		//0EE
		//+AGENT
		int buf=0, bst=INF;
		for(int i=nbs[u]; i; i=nxt[i]){
			int v=dst[i];
			if(v==p)continue;
			buf+=f[v][0][0][1];
		}
		updmin(f[u][1][0][1], buf+1);
		//0AGENT
		updmin(f[u][1][0][0], f[u][1][0][1]);
		buf=0; bst=INF;
		for(int i=nbs[u]; i; i=nxt[i]){
			int v=dst[i];
			if(v==p)continue;
			updmin(bst, f[v][1][0][1]-f[v][1][0][0]);
			buf+=f[v][1][0][0];
		}
		updmin(f[u][1][0][0], buf+bst);
		updmin(f[u][0][0][0], f[u][1][0][0]);
	/* DANGER */
		//1EE
		for(int k=1; k<=m; k++){
			updmin(f[u][0][1][k], f[u][0][1][k-1]);
			//0AGENT
			int buf=0, bst=INF;
			for(int i=nbs[u]; i; i=nxt[i]){
				int v=dst[i];
				if(v==p)continue;
				updmin(bst, f[v][0][1][k-1]-f[v][0][0][k-1]);
				buf+=f[v][0][0][k-1];
			}
			updmin(f[u][0][1][k], buf+bst);
		}
		//0EE
		for(int k=1; k<=m; k++){
			//0AGENT
			updmin(f[u][0][0][k], f[u][0][0][k-1]);
			int buf=0;
			for(int i=nbs[u]; i; i=nxt[i]){
				int v=dst[i];
				if(v==p)continue;
				buf+=f[v][0][0][k-1];
			}
			updmin(f[u][0][0][k], buf);
		}
}

int main(){
	int T;scanf("%d", &T);
	while(T--){
		scanf("%d%d", &n, &m);
		memset(nbs, 0, sizeof(nbs));enm=0;
		for(int i=1; i<n; i++){
			int u, v;
			scanf("%d%d", &u, &v);
			u--;v--;
			add_edge(u, v);
		}
		for(int w=0; w<n; w++){
			for(int x=0; x<B; x++){
				for(int y=0; y<B; y++){
					for(int z=0; z<=m; z++){
						f[w][x][y][z]=INF;
					}
				}
			}
		}
		dp_entry(0, -1);
		ans=f[0][1][1][0];
		printf("%d\n", ans);
	}
	return 0;
}

Problem_[F]riends

timelimit 2 seconds / memory limit 256 megabytes

 

Description

These is an unusual social network. User can not add friends manually in this social network. Friends’ relationship are determined by the system automatically in the following way: Assuming that user X had sent user Y a post at time T1, and user Y sent user X a message at time T2. If T2 > T1 and T2 - T1 <= D,then system determine X and Y is a pair of friends because of that it seems the user Y replied the post which had been sent by X in time.

You are given the log of post-contains N post-records-in chronological order and a number D. Find the number of pairs of users who will be determined to be friends.

 

Input

The first line of the INPUT contain a positive integer T(T<=10) which denotes the number of test cases.

The first line of each case contains two integers N and D (1 ≤ n, d ≤ 1000).

The next N lines of each case contain the post log. The i-th line contains one line of the log formatted as "Xi Yi Ti", which means that user Xi sent a post to user Yi at time Ti (1 ≤ i ≤ n). Xi and Yi are non-empty strings at most 20 characters long, consisting of lowercase letters, and Ti is an integer(0 ≤ Ti ≤ 10000). It is guaranteed that the lines are given in non-decreasing order of Ti's and that no user sent a message to himself. The elements in the lines are separated by single spaces.

 

Output

In each line print an integer k — the number of pairs of friends for each case.

 

Sample Input:

Sample Output:

3

4 3

jki curabis 7

curabis jki 8

masoshonen jki 9

jki masoshonen 15

1 1000

masoshonen masoshojo 0

6 2

a b 1

b a 2

a c 3

a b 4

c a 5

c a 5

1

0

2

 

梗:  ACG-动漫-这不是僵尸吗-魔装少年 /  谁能告诉我Curabis是啥……(我只知道酷拉皮卡) <Update: 哈哈哈哈果然是博熊~~ 感谢教主的解答~~~>

题意:有n条信息发送的指令,从name1发送到name2,发送时间是t,倘若name2在(t, t+d] 时间范围内发送回一条信息,我们称他们两个是盆友(看到了没,回复我信息回复慢了的才不是朋友呢),问在这n条指令中我们能看出有几对朋友。

思路:这里我用到了map的数据结构,map<pair<string,string>,int>,其实只需要用二维数组friends[a][b]=1则代表其为朋友就可以啦(这里我写的有点浪费,f[a][b]=f[b][a]其实不必要,只要时刻将字典序小的作为第一下标就可以了),mpi[a][b]为a给b发送的最后一条信息的时间(这里切记需要注意,时间是可以为0的,需要特判,Sample2就是一个很贴心的提示,因为数组或者各种数据结构初始会默认赋值为0,这样像Sample2这样的就会被判定为有一对朋友)。使用map的话可以直接通过size()函数来获得朋友的组数,数组的话遍历一下就可以啦~,切记当a和b已经是朋友的时候不要再多算一次哦。

Code:

#include <map>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<string,string> pss;

map<pss,int> mpi;
map<pss,int> friends;

void update(string a,string b)
{
    pss f1=make_pair(a,b),f2=make_pair(b,a);
    if(friends[f1]==0 && friends[f2]==0)
    {
        friends[f1]=friends[f2]=1;
        //cout<<a<<":"<<b<<endl;
    }
}

int main()
{
    freopen("F.in","r",stdin);
    int T; scanf("%d",&T);
    for(int _T=0;_T<T;_T++)
    {
        mpi.clear();
        friends.clear();
        int n,d;    scanf("%d%d",&n,&d);
        for(int i=0;i<n;i++)
        {
            string a,b;
            int t;
            cin>>a>>b>>t;
            pss tmp= make_pair(a,b);
            pss ask= make_pair(b,a);
            if(mpi[ask]!=0)
            {
                if(mpi[ask]==-1)
                {
                    if(t-(mpi[ask]+1)<=d && t-(mpi[ask]+1)>0)   //shenti!!! [a!=b]
                    update(a,b);
                }
                else if(t-mpi[ask]<=d && t-mpi[ask]>0)
                {
                    update(a,b);
                }
            }
            if(t!=0)mpi[tmp]=t;
            else mpi[tmp]=-1;
        }
        //for(map<pss,int>::iterator it=friends.begin();it!=friends.end();++it)
        printf("%d\n",friends.size()/2);
    }
    return 0;
}

标程Code:

#include<cstdio>
#include<cstring>
#include<string>
#include<map>
using namespace std;

#define N 2048

int n, m, idx, ans;
int x[N], y[N], t[N];
int mrk[N][N], rig[N][N];
string sx[N], sy[N];

map<string, int> s;

char buf[32];

template<class T> inline void SWAP(T &x, T &y){ T z=x;x=y;y=z; }

int main(){
	int T;scanf("%d", &T);
	while(T--){
		scanf("%d%d", &n, &m);
		s.clear(); idx=0;
		for(int i=0; i<n; i++){
			scanf("%s", buf);sx[i]=buf;s.insert(make_pair(sx[i], 0));
			scanf("%s", buf);sy[i]=buf;s.insert(make_pair(sy[i], 0));
			scanf("%d", &t[i]);
		}
		for(map<string, int>::iterator it=s.begin(); it!=s.end(); it++){
			it->second=idx++;
		}
		memset(mrk, 0, sizeof(mrk));
		memset(rig, 0xc0, sizeof(rig));
		ans=0;
		for(int i=0; i<n; i++){
			x[i]=s[sx[i]];y[i]=s[sy[i]];
			if(i && t[i]>t[i-1]){
				for(int j=i-1; j>=0 && t[j]==t[i-1]; j--){
					rig[x[j]][y[j]]=t[j];
				}
			}
			if(t[i]-rig[y[i]][x[i]]<=m){
				int u=x[i], v=y[i];
				if(u>v)SWAP(u, v);
				if(!mrk[u][v]){
					mrk[u][v]=1;
					ans++;
				}
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}


Problem_[G]raphic

timelimit 2 seconds / memory limit 256 megabytes

 

Description

Curabis always do excellent job on computation geometry problems. Indeed, he has acquired the first solution award about the computation geometry problems in the ACM-ICPC Asia Regional several times. However, JKi, his stupid teammate, do it much worse. 

In order to help poor JKi, Curabis decide provide him with some practice problem set about computation geometry.

One of those problems is this:

Give a set of N2d-points on the plane, is there any three 2d-points of the 2d-point set areable to constructive an triangle, and all 2d-points of this set are either inside of the triangle or on the edge of the triangle?

Would you please help JKi finishing this program?

 

Input

The first line of the INPUT contain a positive integer T(T<=20) which denotes the number of test cases.

The first line of each case contains an integer N.

The next N(3<=N<=1000) lines of each case contain the 2d-points set. The i-th line contains two real numbers Xi, Yi denote the position of the 2d-point.

It is guaranteed that -1000.00<= Xi, Yi <=1000.00, and all real numbers in the input keep two decimal places exactly.

It is guaranteed that all 2d-points in any case can not stand on one straight line over all.

 

Output

In each line print an upper-case letter — ‘Y’ for the triangle is existed, otherwise ‘N’ instead.

 

Sample Input:

Sample Output:

3

4

0.00 0.00

0.00 2.00

2.00 0.00

0.50 0.50

4

0.00 0.00

0.00 2.00

2.00 0.00

2.00 2.00

5

0.00 0.00

0.00 0.00

1.00 0.00

2.00 0.00

1.00 1.00

Y

N

Y

 

梗: 其实我想说这个是不是博熊的ID啊…… <Update>啊啦被我猜对了~~

题意:有好多个点,给所有点的坐标,问是否存在三个点,他们构成的三角形可以包住所有的点(在线上和在点上也算包住)

思路:啊,包住,凸包嘛~ 题目等效于,这些点的凸包定点数是否不大于3,是的话输出Y,反之输出N

(可以作为凸包模板哟)

Code:

#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const double EPS=1e-10;

double add(double a,double b)
{
    if( fabs(a+b) < EPS*( abs(a)+abs(b)) )return 0;
    return a+b;
}

struct P
{
    double x;
    double y;
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator+ (P p)
    {return P(add(x,p.x),add(y,p.y));}
    P operator- (P p)
    {return P(add(x,-p.x),add(y,-p.y));}
    P operator* (double d)
    {return P(x*d,y*d);}
    double dot(P p)//neiji
    {return add(x*p.x,y*p.y);}
    double det(P p)//waiji
    {return add(x*p.y,-y*p.x);}
};

bool cmp_x(const P& p,const P& q)
{
    if(p.x != q.x) return p.x<q.x;
    return p.y<q.y;
}

vector<P> convex_hull(P* ps,int n)
{
    sort(ps,ps+n,cmp_x);
    //for(int i=0;i<n;i++){cout<<ps[i].x<<":"<<ps[i].y<<endl;}
    int k=0;
    vector<P> qs(n<<1);
    for(int i=0;i<n;i++)
    {
        while( k>1 && (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0 )k--;
        qs[k++]=ps[i];
    }
    for(int i=n-2,t=k; i>=0; i--)
    {
        while( k>t && (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0 )k--;
        qs[k++]=ps[i];
    }
    qs.resize(k-1);
    return qs;
}

int N;
P ps[1024];

void solve()
{
    vector<P> qs = convex_hull(ps,N);
    //cout<<qs.size()<<endl;
    if(qs.size()<=3)printf("Y\n");
    else printf("N\n");
}

int main()
{
    freopen("G.in","r",stdin);
    int T; scanf("%d",&T);
    for(int _T=0;_T<T;_T++)
    {
        double tx,ty;
        scanf("%d",&N);
        memset(ps,0,sizeof ps);
        for(int i=0;i<N;i++)
        {
            cin>>tx>>ty;
            //cout<<tx<<":"<<ty<<endl;
            ps[i].x=tx;
            ps[i].y=ty;
        }
        solve();
    }
    return 0;
}

教主标程Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
using namespace std;

#define N 1010

const double eps = 1e-9;

struct pnt{
	double x, y;
	pnt operator+(const pnt &p) const {
		pnt ret; ret.x=x+p.x;
		ret.y=y+p.y; return ret;
	}
	pnt operator-(const pnt &p) const {
		pnt ret; ret.x=x-p.x;
		ret.y=y-p.y; return ret;
	}
	pnt operator*(const double c) const {
		pnt ret; ret.x=x*c;
		ret.y=y*c; return ret;
	}
	pnt operator/(const double c) const {
		pnt ret; ret.x=x/c;
		ret.y=y/c; return ret;
	}
} p[N];

inline double submul(const pnt &p1, const pnt &p2) { return p1.x*p2.y-p2.x*p1.y; }
inline double dist2(const pnt &p1, const pnt &p2){ return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y); }

int main(){
	int T;scanf("%d", &T);
	while(T--){
		int n, a, b, c;
		scanf("%d", &n);
		for(int i=0; i<n; i++)scanf("%lf%lf", &p[i].x, &p[i].y);
		for(int i=a=0; i<n; i++){
			if (p[i].y-p[a].y<-eps || (p[i].y-p[a].y<eps && p[i].x-p[a].x<-eps))a=i;
		}
		for(int i=b=c=0; i<n; i++){
			if (i==0 && a==0){ i++; b++; c++; }
			double sib=submul(p[i]-p[a], p[b]-p[a]);
			if(sib>eps || (sib>-eps && dist2(p[i], p[a])-dist2(p[b], p[a])>eps))b=i;
			double sic=submul(p[i]-p[a], p[c]-p[a]);
			if(sic<-eps || (sic<eps && dist2(p[i], p[a])-dist2(p[c], p[a])>eps))c=i;
		}
		int flag=1;
		for (int i=0; i<n; i++){
			if (submul(p[c]-p[b], p[i]-p[b])<-eps){
				flag=0;
				break;
			}
		}
		if(flag){
			printf("Y\n");
		}else printf("N\n");
	}
	return 0;
}

关于标程,有一个地方没能弄明白……

submul(p[c]-p[b], p[i]-p[b])<-eps

这里的<-符号并没有见过,如果有人知道这个的意思请务必告诉我,谢谢~~


<Update> 教主亲切的给出了答复……

 

哭 啊啊啊啊啊居然是小于负的eps,脸都丢尽了啊啊啊啊啊…… 



啊啦啦~如果有什么我写的不够清楚的或者还有什么想要问的想要提的建议,请务必回复告知我~

我会在看到的第一时间为您解答~







  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

糖果天王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值