这场的体验挺好的,总体感觉题目给自己一些了good idea,不过就是比赛过程中不争气,只做出来了仅仅三道题目(赛后补题+两道
水
#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=2e4+5;
int a[N],b[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,t;
cin>>n;
t=0;
for(int i=1; i<=n; ++i)
{
cin>>a[i];
if(abs(a[i])&1) t++;
else b[i]=a[i]/2;
}
t/=2;
for(int i=1; i<=n; ++i)
{
if((abs(a[i])&1))
{
if(t>0)
{
b[i]=(a[i]+1)/2;
t--;
}
else
{
b[i]=(a[i]-1)/2;
}
}
}
for(int i=1; i<=n; ++i)
cout<<b[i]<<endl;
}
水
思路:按照进入的顺序如果比车 a a a 与比该车之前进去的车 b b b 出来的早,那么车a就超车了
#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=2e5+5;
int in[N],to[N],out[N],num[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",in+i);
to[in[i]]=i;
}
for(int i=1;i<=n;++i){
scanf("%d",out+i);
int ps=to[out[i]];
num[ps]=i;
}
int ans=0,maxx=-1;
for(int i=1;i<=n;++i)
{;
if(num[i]<maxx){
ans++;
}
maxx=max(maxx,num[i]);
}
cout<<ans<<endl;
}
比赛过程中对于每个点 i i i ,找了与点 i i i体积最小的点 j j j, 然后输出这个点对,如果体积为0,就找曼哈距离最小的点
实际上这题只需找曼哈顿最小距离的点即可,上面的基于体积的策略对曼哈顿距离也同样成立
看了T神的题解感觉已经很震撼了,没想到直接递归处理,这个思想也可以扩展到更高维度的解决方案上。
思路:
对于 1 1 1维上的点,我们只需将 x x x排序,相邻的成对输出即可
对于 2 2 2维上的点,我们将它们根据 y y y相等进行分类,这样是一个y内有一堆的x,我们对于每个y,将x数组像解决一维上的成对输出即可,如果总个数为奇数,则将这个存起来与下个y剩下的一起输出。
对于 3 3 3维上的点,我们将他们根据 z z z进行分类,这样每个 z z z内都是个二维的信息了,我们按照 2 2 2维那样输出点对即可,如果剩下一下,就留着与下个 z z z的剩下的输出。
时间复杂度为: O ( d ∗ n ∗ l o g n ) O(d*n*logn) O(d∗n∗logn)
#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=5e4+5;
int p[N][3];
int n;
int solve(vector<int> ps,int k)//0~2
{
//从解决第k维度开始的问题,
if(k==3)
return ps[0];
map<int,vector<int> > oo;
for(int &v:ps){
oo[p[v][k]].push_back(v);
}
vector<int> a;
for(auto& p :oo)
{
int cur=solve(p.second,k+1);
if(cur!=-1)
a.push_back(cur);
}
for(int i=0;i+1<a.size();i+=2)
printf("%d %d\n",a[i],a[i+1]);
if(a.size()%2!=0) return a.back();
else return -1;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1; i<=n; ++i)
for(int j=0; j<3; ++j)
scanf("%d",&p[i][j]);
vector<int> ps(n);
iota(ps.begin(),ps.end(),1);
solve(ps,0);
return 0;
}
首先很容易知道我们将这个环形循环可以拆成线性的,因为更新最大值的原因,所以我们只需复制三次原数组,然后处理,如果对于第 i ∈ [ 1 , n ] i\in[1,n] i∈[1,n]个开始听的歌曲,可以全部听完后面的所有歌曲,那么这个就会无限循环即-1。实际上如果有一个是无限循环,那么所有的都是无限循环,即数组的最小值小于二倍的数组的最大值。
下面我们考虑将数组复制三次后,对于 i ∈ [ 1 , 3 ∗ n ] i\in[1,3*n] i∈[1,3∗n]怎么计算可以听多少歌曲。
对于每个 i i i,coolness 是 a [ i ] a[i] a[i],我们现在要找从这个位置开始,到哪个位置结束。对于 i i i,它的key observe是位置 i i i后面第一个大于 a [ i ] a[i] a[i]的位置 j j j,和位置 i i i 后面第一个小于 a [ i ] 2 \frac{a[i]}{2} 2a[i]的位置k。
T神的题解的key point就是找到这两个关键信息 j , k j,k j,k,然后对 j , k j,k j,k的位置分类讨论得出结果
如果 j < k j
如果 k < j k
问题就迎刃而解了,对于关于每个 i i i的 j , k j,k j,k的位置我们可以ST表最大最小值+二分或线段树+二分或单调栈+二分做
#include
using namespace std;
const int N=5e4+10;
vector<int> a;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
a.resize(n);
for(int i=0; i < n; ++i) cin>>a[i];
int m1,m2;
m1=*max_element(a.begin(),a.end());
m2=*min_element(a.begin(),a.end());
if(m2*2>=m1)
{
for(int i=1; i<=n; ++i) cout<<-1<<" ";
cout<<endl;
}
else
{
vector<int> o(3*n);
vector<int> ans(3*n);
vector<int> st_max,st_min;
for(int i=0; i<n; ++i)
o[i]=o[n+i]=o[2*n+i]=a[i];
int up,down;
for(int i=3*n-1; i>=0; --i)
{
//up : o[j]>o[i] 的最小j (j>i)
//down : 2*o[i] >o[j]的 最小的j
while(!st_max.empty()&&o[st_max.back()] <= o[i])
st_max.pop_back();
while(!st_min.empty()&&o[st_min.back()] >= o[i])
st_min.pop_back();
if(!st_max.empty()) up=st_max.back();
else up=-1;
int th=-1,l=0,r=st_min.size()-1;
while(l<=r)
{
int m=l+r>>1;
if(2*o[st_min[m]]<o[i]){
th=m;
l=m+1;
}
else r=m-1;
}
if(th==-1) down=-1;
else down=st_min[th];
if(up==-1&&down==-1) ans[i]=3*n-i;
else if(up==-1) ans[i]=down-i;
else if(down==-1) ans[i]=up-i+ans[up];
else if(up<down)
{
ans[i]=up-i+ans[up];
}
else
ans[i]=down-i;
st_max.push_back(i);
st_min.push_back(i);
}
for(int i=0; i<n; ++i ) cout<<ans[i]<<" ";
cout<<endl;
}
}