I - Inverted Deck
题意:让你选一个区间进行翻转,问能不能是数组递增
思路:我们枚举每个数 a i a_i ai,如果存在一个最大的 j ∈ [ i + 1 , n ] j\in[i+1,n] j∈[i+1,n]且 a j < a i a_j
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int n,a[N];
int l,r;
set<pair<int,int>>s;
int chec(){
for(int i=2;i<=n;i++){
if(a[i]<a[i-1])return 0;
}
return 1;
}
int main() {
ios::sync_with_stdio(false);
cin>>n;l=n+1,r=0;
for(int i=1;i<=n;i++)cin>>a[i],s.insert({a[i],-i});
for(int i=1;i<=n;i++){
auto x=*s.begin();
if(x.fi<a[i]){
int l=i,r=-x.se;
reverse(a+l,a+1+r);
if(chec()){
return cout<<l<<' '<<r,0;
}else{
return cout<<"impossible\n",0;
}
}
s.erase({a[i],-i});
}
cout<<"1 1\n";
//1 3 2
return 0;
}
F - Firetrucks Are Red
大意:n个人,每个人都有喜欢的数字,如果两个人喜欢相同的一个数字,那么称这两个人有直接联系,如果两个不直接联系的人与同一个人有直接联系,则称这两个人为间接联系。问n个人互相能不能成为直接或间接联系。
思路:建图跑一个生成树即可。
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int n,c[N],cnt;
struct uzi{
int s,t,d;
}p[N];
int vis[N];
vector<int>v[N],g[N];
vector<uzi>ans;
int f[N];
int find(int x){
return f[x]==x?x:(f[x]=find(f[x]));
}
int main() {
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
f[i]=i;
int m;
cin>>m;
for(int j=1;j<=m;j++){
int s;
cin>>s;
c[++cnt]=s;
v[i].pb(s);
}
}
sort(c+1,c+1+cnt);
int len=unique(c+1,c+1+cnt)-c-1;
for(int i=1;i<=n;i++){
for(auto &k:v[i]){
k=lower_bound(c+1,c+1+len,k)-c;
g[k].pb(i);
}
}
int cnt=0;
for(int i=1;i<=len;i++){
for(int j=1;j<g[i].size();j++){
p[++cnt]={g[i][j],g[i][j-1],c[i]};
}
}
for(int i=1;i<=cnt;i++){
int x=find(p[i].s),y=find(p[i].t);
if(x==y)continue;
f[x]=y;
ans.pb({p[i]});
}
if(ans.size()==n-1){
for(auto k:ans){
cout<<k.s<<' '<<k.t<<' '<<k.d<<'\n';
}
}else{
cout<<"impossible\n";
}
return 0;
}
H. Height Profile
大意:有一个山,给你每公里位置上的高度。让你求坡度 ≥ k \geq k ≥k的最长连续水平长度,定义坡度 k k k为 垂 直 高 度 水 平 长 度 \frac{垂直高度}{水平长度} 水平长度垂直高度.
思路: h j − h i j − i ≥ k \frac{h_j-h_i}{j-i}\geq k j−ihj−hi≥k转化这个式子, h j − k j ≥ h i − k i h_j-kj\geq h_i-ki hj−kj≥hi−ki
那么定义数组 b i = h i − k i b_i=h_i-ki bi=hi−ki,排序找到每个位置能向左延伸的最长长度。
由于左右还能延伸一定的距离,所有分情况讨论一下(延伸必然延伸到斜率大的一边)。二分找出延伸的距离即可。
ps:一开始排序的地方用线段树实现的…
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int n,k,a[N],b[N],f[N],can[N];
const int inf=1e9;
void gao(){
for(int i=0;i<=n;i++){
f[i]=i;
can[i]=inf;
}
sort(f,f+1+n,[](int x,int y){
if(b[x]==b[y])return x<y;
return b[x]<b[y];
});
int l=f[0];
for(int i=1;i<=n;i++){
if(f[i]<l){
l=min(l,f[i]);
continue;
}
can[f[i]]=l;
l=min(l,f[i]);
}
}
int main() {
double ch;
scanf("%d%d",&n,&k);
for(int i=0;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=k;i++){
double l;
scanf("%lf",&l);
int L=l*10+0.5;
long double ans=-1;int sta=0;
for(int j=0;j<=n;j++){
b[j]=a[j]-L*j;
if(j&& a[j]-a[0]>=L*j )ans=j,sta=1;
}
gao();
for(int j=1;j<=n;j++){
int ne=can[j];
if(ne==inf)continue;
sta=1;
if(!ne){
if(j==n)ans=max(ans,(long double)(j-ne));
else{
long double l=j,r=j+1,can=j;
long double k=a[j+1]-a[j];
for(int xa=1;xa<=30;xa++){
long double _mid=(l+r)/2;
long double n=a[j]+k*(_mid-j);
if((n-a[ne])>=L*(_mid-ne))can=_mid,l=_mid;
else r=_mid;
}
ans=max(ans,can-ne);
}
}else{
long double k1=a[ne]-a[ne-1];
long double k2=a[j+1]-a[j];
if(ne){
long double l=ne-1,r=ne,can=ne;
long double k=a[ne]-a[ne-1];
for(int xa=1;xa<=30;xa++){
long double _mid=(l+r)/2;
long double n=a[ne-1]+k*(_mid-ne+1);
if((a[j]-n)>=L*(j-_mid))can=_mid,r=_mid;
else l=_mid;
}
ans=max(ans,j-can);
}
if(j+1<=n){
long double l=j,r=j+1,can=j;
long double k=a[j+1]-a[j];
for(int xa=1;xa<=30;xa++){
long double _mid=(l+r)/2;
long double n=a[j]+k*(_mid-j);
if((n-a[ne])>=L*(_mid-ne))can=_mid,l=_mid;
else r=_mid;
}
ans=max(ans,can-ne);
}
}
}
if(!sta)puts("-1");
else printf("%.10Lf\n",ans);
}
return 0;
}
A. Average Rank
大意:n个人 初始每个人都是0分,m场比赛,每次比赛有k个人得一分。
定义排名为,按分数由大到小排序,排名递增,同分同排名。
思路:显然每个人的分数是逐渐递增的(每次+1/0),那么我们可以很轻松的知道多于某个分数 x x x的人数 c n t x cnt_x cntx,设 s u b x sub_x subx为第 i i i次操作前,分数 x x x的排名之和。设 s u b x sub_x subx上一次更新的位置为 l a s t x last_x lastx,那么在 l a s t x − > i − 1 last_x->i-1 lastx−>i−1的操作中,分数 x x x的排名都是不变的,那么显然可以很轻松的计算出 s u b x sub_x subx变大了多少。
每个人在每场加分比赛后,分数都会变化,那么在他上一次变化到前一次中的分数 x x x都没变,那么我们可以通过记录这个人第一次变成 x x x分之前的 s u b x sub_x subx来获得他在两次更新中间真正在分数 x x x下获得的排名之和(注意此时未把最新一次操作的排名算进去)。
最后的时候,把所有分数在m次比赛后的 s u b sub sub算出来,再更新一下每个人的 s u m sum sum,因为之前计算的排名都是从0开始的,所以每个人的排名之和要加m。
#include
using namespace std;
typedef long long LL;
const int N = 2e6 + 10;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int n,w;
int cnt[N],last[N],p[N];
LL sum[N],sub[N],pre[N];
int a[N];
int main() {
ios::sync_with_stdio(false);
cin>>n>>w;
for(int i=1;i<=w;i++){
int k;
cin>>k;
for(int j=1;j<=k;j++){
int x;
cin>>x;
//他的分数是 p[x]
sub[p[x]]+=1ll*(i-last[p[x]])*cnt[p[x]];
//更新这个分数的排名 的和
++cnt[p[x]];//人数++
last[p[x]]=i;//最后一次更新 p[x]的 分 是第i个操作
sum[x]+=sub[p[x]]-pre[x];//更新 到这个人上去
++p[x];//+分
sub[p[x]]+=1ll*(i-last[p[x]])*cnt[p[x]];//更新这个分数的和
last[p[x]]=i;
pre[x]=sub[p[x]];
}
}
for(int i=1;i<=n;i++){
sub[p[i]]+=1ll*(w+1-last[p[i]])*cnt[p[i]];
last[p[i]]=w+1;
sum[i]+=sub[p[i]]-pre[i]+w;
}
cout<<fixed<<setprecision(8);
for(int i=1;i<=n;i++){
cout<<1.0*sum[i]/w<<'\n';
}
return 0;
}