noip模拟59
A. 柱状图
很容易发现如果确定某个点一定是屋顶的话,那么答案是随 ta 的高度成单谷变化的.
所以可以选择枚举每个点,然后树状数组\(+\)三分.
但是我们发现对于这种曲线直接退火就可以,准确率很高,跑得也很快.
另外还可以线段树\(+\)二分,发现屋顶是一个 △,于是可以选择二分上面下面有多少个点.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll long long
#define ull unsigned ll
#define lf long double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll res=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return cit?res:-res;
}
} using namespace BSS;
const ll N=1e5+21;
const long long int inf=1e15;
ll m,n,cntx,cnty,ans,maxh,maxn;
ll h[N],wx[N],wy[N],lshx[N],lshy[N];
struct Bit_Array{
ll res; ll c[N<<3];
inline void update(ll x,ll w){
for(;x<=maxn;x+=lbt(x)) c[x]+=w;
}
inline ll query(ll x){
res=0; for(;x;x&=x-1) res+=c[x]; return res;
}
}A,B,C,D;
inline ll calc(ll x,ll t){
if(t-x+1<=0 or t-n+x<=0) return inf;
ll res=0,tmp;
tmp=ub(lshx+1,lshx+1+cntx,t-x)-lshx-1;
res+=A.query(tmp)*(t-x)-B.query(tmp);
res+=B.query(n)-B.query(tmp)-(A.query(n)-A.query(tmp))*(t-x);
tmp=ub(lshy+1,lshy+1+cnty,t+x)-lshy-1;
res+=C.query(tmp)*(t+x)-D.query(tmp);
res+=D.query(n)-D.query(tmp)-(C.query(n)-C.query(tmp))*(t+x);
// cout<<x<<" "<<t<<' '<<res<<endl;
return res+abs(t-h[x]);
}
inline ll solve(ll x){
ll l=1,r=maxh+n,lmid,rmid,vall,valr,res=inf;
while(l<=r){
rmid=(l+r)>>1,lmid=rmid-1;
if(lmid<=l or rmid>=r) break;
vall=calc(x,lmid),valr=calc(x,rmid);
vall<valr ? r=rmid : l=lmid ;
}
for(int i=l;i<=r;i++) res=min(res,calc(x,i));
return res;
}
signed main(){
File(c);
n=read(),ans=inf;
for(int i=1;i<=n;i++) maxh=max(maxh,h[i]=read());
for(int i=1;i<=n;i++){
lshx[i]=wx[i]=h[i]-i,lshy[i]=wy[i]=h[i]+i;
}
sort(lshx+1,lshx+1+n),cntx=unique(lshx+1,lshx+1+n)-lshx-1;
sort(lshy+1,lshy+1+n),cnty=unique(lshy+1,lshy+1+n)-lshy-1;
maxn=max(cntx,cnty)+n;
for(int i=n;i>=1;i--){
wx[i]=lb(lshx+1,lshx+1+cntx,wx[i])-lshx;
wy[i]=lb(lshy+1,lshy+1+cnty,wy[i])-lshy;
C.update(wy[i],1),D.update(wy[i],h[i]+i);
}
for(int i=1;i<=n;i++){
C.update(wy[i],-1),D.update(wy[i],-h[i]-i);
// cout<<calc(i,1000)<<endl;
ans=min(ans,solve(i));
A.update(wx[i],1),B.update(wx[i],h[i]-i);
}
printf("%lld\n",ans),exit(0);
}
B. 应急棍
找规律,高精小数没有写.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll long long
#define ull unsigned ll
#define lf double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:-w;
}
} using namespace BSS;
ll m,n,C;
ll pow2[40],pow4[40];
auto Work=[]()->void{
ll op=read(); lf x,y;
if(op==1) x=0,y=0; if(op==2) x=n,y=n;
if(op==3) x=0,y=n; if(op==4) x=n,y=0;
if(op<=4) return printf("%.10lf %.10lf\n",x,y),void();
ll now=0; op-=4;
while(op>pow4[now]+2*pow2[now]*(pow2[now]+1)) op-=pow4[now]+2*pow2[now]*(pow2[now]+1),now++;
lf len=n/((lf)pow2[now]);
if(op<=pow4[now]) x=len/2.0+(op-1)/pow2[now]*len,y=len/2.0+(op-1)%pow2[now]*len;
else{
op-=pow4[now],x=(op-1)/(pow2[now+1]+1)*len;
if((op-1)%(pow2[now+1]+1)>=pow2[now]) x+=len/2.0,y=((op-1)%(pow2[now+1]+1)-pow2[now])*len;
else y=(op-1)%(pow2[now+1]+1)*len+len/2.0;
}
printf("%.10lf %.10lf\n",x,y);
};
signed main(){
File(a);
pow2[0]=1,pow4[0]=1;
for(ll i=1;i<=35;i++) pow2[i]=pow2[i-1]<<1ll,pow4[i]=pow4[i-1]<<2ll;
C=read(); int Ts=read(); n=read();
while(Ts--) Work();
exit(0);
}
C. 擒敌拳
考场上测试点分治切了,单调队列 \(+\) 斜率优化.
每次枚举队列里的所有元素就行了.
正解是李超线段树.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll long long int
#define ull unsigned ll
#define lf double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define FILE(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
int ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=2e5+21;
namespace DEQUE{
struct Deque{
ll l,r; ll q[N<<2];
inline bool empty(){ return l>r; }
inline ll size(){ return r-l+1; }
inline ll front(){ return q[l]; }
inline ll back(){ return q[r]; }
inline void pop_front(){ l++; }
inline void pop_back(){ r--; }
inline void push_front(ll x){ q[--l]=x; }
inline void push_back(ll x){ q[++r]=x; }
} que;
} using namespace DEQUE;
ll m,n,maxh,ans,flag;
ll H[N];
struct I { ll h,id,lc; } p[N];
inline void Work1(){
ll now;
for(int i=1;i<=n;i++){
maxh=max(maxh,p[i].h),p[i].lc=i;
if(que.empty()) que.push_back(i),ans=max(ans,p[i].h);
else{
if(p[que.back()].h<p[i].h) que.push_back(i);
else{
while(que.size() and p[que.back()].h>=p[i].h){
now=que.back(),que.pop_back();
p[i].lc=min(p[now].lc,p[i].lc);
}
que.push_back(i);
}
for(int j=que.l;j<=que.r;j++){
ans=max(ans,(i-p[que.q[j]].lc+1)*p[que.q[j]].h);
}
}
printf("%lld ",max(ans,maxh));
}
}
inline ll caly(ll x){ return x*H[x]-H[x]; }
inline lf getxl(ll a,ll b){ return 1.0*(caly(a)-caly(b))/(H[a]-H[b]); }
inline void Work2(){
for(int i=1;i<=n;i++) H[i]=p[i].h;
que.push_back(1); ll res=0; printf("%lld ",H[1]);
for(int i=2;i<=n;i++){
while(que.l<que.r and getxl(que.q[que.l+1],que.front())<=i) que.pop_front();
res=max(H[i],H[que.front()]*i-caly(que.front()));
while(que.l<que.r and getxl(i,que.back())<=getxl(que.back(),que.q[que.r-1])) que.pop_back();
que.push_back(i);
printf("%lld ",res);
}
}
signed main(){
FILE(b);
n=read(),que.l=n+1,que.r=n;flag=1;
for(int i=1;i<=n;i++) p[i].h=read(),flag&=(p[i].h>=p[i-1].h);
if(flag) Work2();
else Work1();
exit(0);
}
D. 边数
容易发现是一个基环内向树.
基环树上不断环选择倍增是一个很套路的想法.
于是就完了.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
// #define int long long
#define lf long double
#define pb push_back
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define lbt(x) ((x)&(-(x)))
#define Fill(x,y) memset(x,y,sizeof(x))
#define Copy(x,y) memcpy(x,y,sizeof(x))
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
auto read=[]()->int{
int w=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) w=(w<<3)+(w<<1)+(ch^48),ch=getchar();
return cit?w:(-w);
};
} using namespace BSS;
const int N=5e5+21,inf=0x3f3f3f3f;
int m,n,ts,ans,cnt,lg2;
int d[N],to[N],cir[N],vis[N],dep[N],nxt[N],rk[N<<1];
int dp[N][20];
vector<int> vec[N];
queue<int> que;
auto Put=[]()->void{
for(int i=1;i<=n;i++) cout<<vis[i]<<' ';
puts("");
};
auto SpCalc=[](int x)->bool{
for(auto i : vec[x]) if(!vis[i]) return 1;
return 0;
};
auto solve=[](int a)->int{
int nx=vec[a].size(),ny=nx<<1,x,y,z=inf;
for(int i=1;i<=nx;i++) rk[i]=vec[a][i-1],rk[i+nx]=rk[i];
for(int i=1,j=1;i<=ny;i++,j=max(i,j)){
while(vis[rk[j]]) j++;
nxt[i]=j; Fill(dp[i],0);
}
for(int i=1;i<=ny;i++) dp[i][0]=nxt[nxt[i]+m];
for(int j=1;j<=lg2;j++){
for(int i=1;i<=ny;i++)
dp[i][j]=dp[dp[i][j-1]][j-1];
}
// nxt 表示该连哪一个了.
// dp[i][j] 表示从 i 这个点开始连,连 2^j 边,下一个应该够哪里.
// for(int j=1;j<=lg2;j++){
// for(int i=1;i<=ny;i++) cout<<dp[i][j]<<' ';
// puts("");
// }
for(int i=1;i<=nx;i++){
x=i,y=0;
for(int j=lg2;~j;j--)
if(dp[x][j] and dp[x][j]<=i+nx) y|=1<<j,x=dp[x][j];
y+=(x<i+nx),z=min(z,y);
}
return z;
};
auto Tope=[]()->void{
int u,v; dep[1]=0,dep[to[1]]=1;
for(int i=2;i<=n;i++) if(!d[i]) dep[i]=1,ans++;
for(int i=2;i<=n;i++) if(dep[i]==1) que.push(i);
while(que.size()){
u=que.front(),que.pop(),v=to[u];
if(dep[u]>m) dep[u]=1,ans++;
dep[v]=min(dep[u]+1,dep[v]);
if(!(--d[v])) que.push(v);
}
};
auto Bfs=[]()->void{
int u,v; vis[1]=1; assert(que.empty());
for(int i=2;i<=n;i++) if(dep[i]==1) que.push(i);
while(que.size()){
u=que.front(),que.pop(),v=to[u],vis[u]=1;
if(vis[v]) continue;
que.push(v),dep[v]=min(dep[v],dep[u]+1);
}
};
signed main(){
File(d);
n=read(),m=read(),lg2=log2(n)+1; int u,v;
for(int i=1;i<=n;i++){
u=read(),to[u]=read(),d[to[u]]++;
}
for(int i=1;i<=n;i++){
if(vis[i]) continue;
u=i;
while(!vis[u]) vis[u]=i,u=to[u];
if(vis[u]!=i) continue;
v=u,u=to[u],cir[v]=++cnt,vec[cnt].pb(v);
while(u!=v) cir[u]=cnt,vec[cnt].pb(u),u=to[u];
}
Fill(vis,0),Fill(dep,0x3f); Tope(),Bfs();
for(int i=1;i<=n;i++) if(dep[i]>m) vis[i]=0;
for(int i=1;i<=cnt;i++){
if((int)vec[i].size()<m) { ans+=SpCalc(i); continue; }
if(!SpCalc(i)) continue;
ans+=solve(i);
}
printf("%d",ans),exit(0);
}