前言
人在极度无语的情况下是会笑出来的。
——灰喜鹊Cyanopica
A. Pairing
给定4个球,球可能是1、2、3、4这四种颜色中的一种。现在让你把球拿走,但你得拿相同颜色的球,而且最少得拿走两个球。请问最多能拿几次?
分析一下。
在这里借鉴一下tourist的思路(游客你带我走吧~你带我走吧~),先把四个代表颜色的数字输入到数列里,然后把它们排序。这样,相同的数会紧挨着。那么就可以比较方便地判断情况:
实际情况 | 推导思路 | 推导情况 | 结果 |
---|---|---|---|
四个值都相等 | 没啥,就是0=1=2=3 |
a[0] == a[1] && a[1] == a[2] && a[2] == a[3] | cnt = 1 |
三个值相等 | 并非四个都相等时,分为前三个或者后三个相等的情况,但都会在同一次被拿走;所以只要判断有0=1或者2=3就成,1=2是必然的,不用管 |
a[0] == a[1] || a[2] == a[3] | cnt = 1 |
两个值相等 | 并非四个都相等时,两对两个值相等,0=1以及2=3而且1!=2 |
a[0] == a[1] && a[2] == a[3] && a[1] != a[2] | cnt = 2 |
并非四个都相等时,只有两个值相等,分为三种情况:0=1,1=2,2=3 |
a[0] == a[1] || a[1] == a[2] || a[2] == a[3] | cnt = 1 |
后面的情况都与第一种情况冲突,所以第一种情况写if,后边写else。不难注意到第二个情况和第四个情况其实可以合并(a[1] == a[2]的或条件对第二个情况来说有没有都一样),所以可以拿它俩写else if。剩下的那个情况写else。
代码如下——
int a[4] = {-1};
for (int i = 0; i < 4; i ++)
{
cin >> a[i];
}
sort(a, a+4);
int rmb = 0; int cnt = 0;
if (a[0] == a[1] && a[1] == a[2] && a[2] == a[3])
cnt = 1;
else if (a[0] == a[1] && a[2] == a[3] && a[1] != a[2])
cnt = 2;
else
{
if (a[0] == a[1] || a[1] == a[2] || a[2] == a[3])
cnt = 1;
else; //写这个else是个人习惯问题
}
做题教训&&笑点解析
布什戈门你打个比赛都不用草稿纸的吗啊?真当自己最强大脑?遇见需要分类讨论或者找边界等等需要细节的地方时,甭管容易还是困难都给我沉下心来分析打演草!
要不然就会出现卡某个测试点的情况——没思考周全。
B. Garbage Collection
我没开出来B,原因很简单,没读懂题……
题意是:
在A市,垃圾分类成N种,N大于等于1小于等于100。
第1行被用来输入N,第i种垃圾的回收方式写在第i+1行,方式是——某天的日期模除以给定的q时,刚好余数是给定的r,那么这个某天就是这种垃圾的回收日。r和q大于等于0小于等于1e9。
再给你一个Q大于等于1小于等于100,设现在有Q份垃圾,对于第t种垃圾(当然t大于等于1小于等于N),问你在给定的第d天之后的第几天才能扔,这个d大于等于0小于等于1e9。
难吗?不好理解是吗?那我们换一个说法。
按照A市的垃圾分类方法可以分出N种垃圾,其中N大于等于1小于等于100。
第i种垃圾的回收方式写在第i+1行(第1行被用来输入N),方式是——已知某一次收垃圾的日子是第r天,再过q天后垃圾车才会再来一趟。r和q很不合常理地大于等于0小于等于1e9。
现在我有Q份垃圾得扔,Q大于等于1小于等于100。
在第d天的时候我想扔第t种垃圾;这个d大于等于0小于等于1e9,t大于等于1小于等于N。现在问你,从第d天开始算,之后最近的扔垃圾的日子是第几天。
因为我们只是已知有这么一个第r天,它不一定比后来给定的第d天来的要早,也有可能比第d天来得晚,要是后者就不用非得等第r天后下一趟垃圾车什么时候来了,而是可以直接等到第r天。
我在重做这道题的时候想用tuple去做,结果发现人家数据是分段给的,没法做好像。没办法,只能含泪笑抄袭一下zyc老师的答案。
typedef long long ll;
const ll N=200005;
const ll INF=0x3f3f3f3f3f3f3f3f;
ll n , q;
ll num[N][2];
void solve(){
cin >> n;
for (ll i = 1; i <= n; i++){
cin >> num[i][0] >> num[i][1];
//写进两种数组里,[i][0]是q,[i][1]是r
}
cin >> q;
while(q--){
ll a , b;
cin >> a >> b; //a是t,b是d
ll c = b % num[a][0];
//把r加上好几个q加到让r'大于d,跟把d减去好几个q从而使d'小于r的效果是等价的。
//c就是减去好几个q之后剩下的比q小的值,也就是d'。
if(c <= num[a][1]){
cout << b + num[a][1] - c<< '\n';
//c比r小,说明这个d'离下一个r不远。
//打印b+r-c,r-c就是d'距离下一个r的距离。
//等于就更不必说了。
}
else {
cout << b + num[a][1] + num[a][0] - c << '\n';
//c比r大,说明这个d'有点超,刚刚巧了,垃圾车前两天刚走,得等下下一个r。
//打印b+q+r-c,q就是多等的那一个循环。
}
}
}
signed main(){
ll t = 1; //cin >> t;
while (t--) solve();
return 0;
}
做题教训&&笑点解析
就怎么说呢……思维要活,画画图,用用草纸。画个示意图对我来说挺有用的。
下次看不明白英文跟机翻要不直接看日文吧(
C. Repeating
给定N个正整数,N大于等于1小于2e5,正整数们大于等于1小于等于1e9。
按给定顺序输出这些数;求某一个数出现时,这串按特定顺序排序的数里面,前一个和它大小相等的数的位置。如果是第一次出现,那么位置定为-1。
分析一下。
就是对输入的数列进行循环查找,查找到了某个数b对应的前一个数a的位置就输出,然后把这个数b看作下一个可能存在的数c所对应的前一个数,参与下一轮循环。
可是这个数组不可能开得这么大,用vector还超时严重,不如用map。
map里面绑定两种数值,前一个是某个数字的数值,用来充当key;后一个是这个数字对应的位置,用来充当value(可以想一下这个key和value的选取,这个思维很重要)。
map本来就可以当数组,只不过是一个一个一个地登记式输入——先存入temp缓存,再判断这个temp之前有没有存在过,即map[temp]的值是否不为0。如果不为0,那就把它对应的那个值,也就是前一个与它相等的数的位置,给打印出来,再改成现在这个新数所在的位置,以供下次使用;如果为零,就打印-1,再改成现在这个新数所在的位置,以供下次使用。
这样就不至于真的要存10,0000,0000个数了,只需要存出现过的那几类数就好了,而且重复出现时,把value一改就完事,能占一个位置就绝对不占两个位置。这样你看,空间也节省了,时间也节省了,多好。
代码如下——
map<int, int> m; int n = 0;
cin >> n; int temp = 0;
for (int i = 1; i <= n; i ++)
{
cin >> temp;
if (m[temp] != 0) cout << m[temp] <<" ";
else
cout << "-1 ";
m[temp] = i; //简化一下,既然都得存新位置,那就放if/else后面就行
}
//感谢gzx老师,我爱西红柿
做题教训&&笑点解析
人不能在一块石头上跌倒两次,你这都第几次了?啊?事不过三!map!学会用好map!憋老惦记着你那破数组了!
另外这个map反过来“先有数值再找位置”的思想确实是跟数组“先有位置再找数值”不一样。我不知道这种思维怎么练,但是我知道我得先积累着。
小结
D题得要dfs……天哪我连树都没搞懂你让我搞这个……任重而道远啊……
感觉打这个ACM能每天给我过个sancheck,过几天说不定我就能在现实生活中看见爱慕织姬了嘿嘿嘿