Speedforces,C题速度卡了,F题没想到,然后就凉了。
总结
- 如果需要对多属性的元素按一个属性进行排序,删除,lower_bound等操作时,可以选择建立结构体扔到set里,也可以选择用map:键就是排序的属性,值可以是结构体,也可以是下标,这样实现的难度会远小于set。
- 又一次双指针,for套while,也许之后会系统的总结一下。
- 思维:考虑哪些状态一定比哪些状态优(控制关系),从而减少需要排查的状态个数。
1041A. Heist
排序,输出最后一项减第一项-n+1
1041B. Buying a TV Set
给x,y约分,输出min(a/x,b/y)
1041C. Coffee Break 贪心
给定长为n的整数序列,表示需要在这些时刻进行喝茶,一天最多有m分钟。每次喝茶持续一分钟,给定两次喝茶间的最小间隔d,问最少几天可以皮皮完,按输入顺序输出每次喝茶所在天数。
显然贪心,每次选择大于已选值d以上的第一个,当天放不下就新开一天。如果输出答案只需输出最少天数,使用set维护这些时刻即可。但是需要记录答案,所以用map来写。
int ans[M];
int main(void)
{
int n = read(),m=read(),d=read();
map<int,int> mp;
for(int i=1;i<=n;i++)
mp[read()] = i;
int day = 0;
while(!mp.empty())
{
day++;
int st = 0;
map<int,int>::iterator it;
while((it = mp.lower_bound(st))!=mp.end())
{
ans[it->second] = day;
st = it->first+d+1;
mp.erase(it);
}
}
printf("%d\n",day );
for(int i=1;i<=n;i++)
printf("%d ",ans[i] );
return 0;
}
1041D. Glider 双指针:滑动窗口
首先肯定在风口的最左边开始,而且飞行的路程 = 高度 + 可以走的风口距离。如果从左到右枚举起始的风口,可以走的最后一个风口编号也是单调不减的,每次检查一下即可。
ll wind[M],sumwind[M];
ll fall[M];
int main(void)
{
ll n = read(), h = read();
ll x1 = read(), x2 = read();
for(int i=1;i<n;i++)
{
wind[i] = x2 - x1;
ll p1 = read(), p2 = read();
fall[i] = p1 - x2;
x1 = p1, x2 = p2;
}
wind[n] = x2 - x1;
for(int i=1;i<=n;i++)
sumwind[i] = sumwind[i-1] + wind[i];
ll ans = 0, cost = 0, up = 0;
for(int i=1;i<=n;i++)
{
cost -= fall[i-1];
while(up<n&&cost<h)
{
cost += fall[++up];
}
ans = std::max(ans,sumwind[up]-sumwind[i-1]);
}
printf("%I64d\n",ans+h );
return 0;
}
1041E. Tree Reconstruction 构造,树
首先所有的对必须一个是n,另一个不是n,然后从1到n-1计算一下它们个数的前缀和,如果sum[j]>j则也说明不行。
原理是从1到n-1所有的数都本该出现一次,如果没有出现,就说明它至少有两边连着比他都大的数。
构造一条链,从1到n-1遍历个数,如果为0就放在队列里,如果为1就放在链右端,大于1就先放在链右端,再在右端添加r-1个队列里的数。最后将n放在最右端。
int save[M];
int ans[M];
int main(void)
{
int n = read(), fail = 0;
for(int i=1;i<n;i++)
{
int a = read(), b = read();
if(a==n&&b==n) fail = 1;
if(a!=n&&b!=n) fail = 1;
if(a==n) save[b]++;
if(b==n) save[a]++;
}
for(int i=1,sum=0;i<n;i++)
{
sum += save[i];
if(sum>i) fail = 1;
}
if(fail) return !printf("NO\n");
queue<int> die;
int j = 1;
for(int i=1;i<n;i++)
{
if(save[i]==0)
die.push(i);
else
{
ans[j++] = i;
for(int k=1;k<save[i];k++)
{
ans[j++] = die.front();
die.pop();
}
}
}
ans[n] = n;
printf("YES\n");
for(int i=1;i<n;i++)
{
printf("%d %d\n",ans[i],ans[i+1] );
}
return 0;
}
1041F. Ray in the tube 思维
问题1:上下两条线(1e9)上分布着若干传感器(1e5),选择下方线上的一个整点A,上方线上的一个整点B,从A到B发射一道可以反射的光,问光最多经过几个传感器(A,B上的也算)
想不出来
问题2:有一条线上有若干个传感器,选择线上一个整点A,一个间距C(C>=2且为偶数),发射一个跳弹,跳弹会经过A,A+C,A+2C,…,问最多经过几个传感器。
想不出来
问题3:有一条线上由若干个传感器,给定间距C,选择线上的一个整点A发射跳弹,跳弹会经过A,A+C,A+2C,…,问最多经过几个传感器。
给所有传感器的坐标取模,答案就是最多的模值分布。
一个结论是,间距C的情况会比间距xC的情况更优(x>1)。
在两条线的时候,画一下会发现,间距C的情况会比间距xC的情况更优(x为奇数),所以只需要枚举C=1,2,4,8,16,…即可。每次枚举时把模数分布放在map里。
总复杂度
O
(
n
l
o
g
(
1
e
9
)
l
o
g
n
)
O(nlog(1e9)logn)
O(nlog(1e9)logn)
int A[M],B[M];
int main(void)
{
int n = read(); read(); for(int i=0;i<n;i++) A[i] = read();
int m = read(); read(); for(int i=0;i<m;i++) B[i] = read();
int ans = 2;
for(int step = 2;step<=MOD;step<<=1)
{
int tmp = 0;
std::map<int,int> mp;
for(int i=0;i<n;i++)
tmp = std::max(tmp,++mp[A[i]%step]);
for(int i=0;i<m;i++)
tmp = std::max(tmp,++mp[(B[i]+(step>>1))%step]);
ans = std::max(ans,tmp);
}
printf("%d\n",ans );
return 0;
}
附:这个问题2我没想到做法。