OI 笑传 #9

是一些有意思的小思维。

CF1915F

看上去有点吓人,但是实际上由于每个人的行进速度和方向都一样,因此行进过程中每个人都不会碰上其他人。

于是两个人打招呼只发生在一人停在终点另一人经过时,也就是对终点排序后的一个类逆序对状物。

于是线段树树状数组什么的乱搞即可。

code

Show me the code
#define psb push_back
#define mkp make_pair
#define ls p<<1
#define rs (p<<1)+1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
  ll x=0,f=1;
  char c=getchar();
  while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
  while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
  return x*f;
}
const int N=5e5+5;
int n;
int cnt=0;
int hmax=0;
struct work{
  int v;
  int t;
  int id;
}wk[N];
pair<int,int> ru[N];
struct seg{
  int l;
  int r;
  int t1;int t2;
  ll v;
}t[N<<2];
map<int,int> mp;
void b(int p,int l,int r){
  t[p].l=l;t[p].r=r;
  hmax=max(hmax,p);
  if(l==r){
    t[p].t1=t[p].t2=0;
    t[p].v=0;
    return ;
  }
  int mid=l+r>>1;
  b(ls,l,mid);b(rs,mid+1,r);
  return ;
}
void asiend(int p,int pos){
  if(t[p].l==t[p].r&&t[p].r==pos){
    t[p].t2=1;return ;
  }
  int mid=t[p].l+t[p].r>>1;
  if(pos<=mid)asiend(ls,pos);
  if(mid<pos) asiend(rs,pos); 
  return ;
}
void asibeg(int p,int pos){
  if(t[p].l==t[p].r&&t[p].r==pos){
    t[p].v=1;return ;
  }
  int mid=t[p].l+t[p].r>>1;
  if(pos<=mid)asibeg(ls,pos);
  if(mid<pos) asibeg(rs,pos);
  t[p].v=t[ls].v+t[rs].v;
  return ;
}
ll qry(int p,int l,int r){
  if(l<=t[p].l&&t[p].r<=r){
    return t[p].v;
  }
  int mid=t[p].l+t[p].r>>1;
  if(l<=mid&&mid<r)return qry(ls,l,r)+qry(rs,l,r);
  if(l<=mid)return qry(ls,l,r);
  if(mid<r) return qry(rs,l,r);
  return 0;
}
bool cmp(work x,work y){return x.v<y.v;}
void init(){
  for(int i=1;i<=hmax;i++){
    t[i]={0,0,0,0,0};
  }
  for(int i=1;i<=n;i++){
    wk[i]={0,0,0};
    ru[i].first=ru[i].second=0;
  }
  mp.clear();
  cnt=hmax=0;
  return ;
}
void solve(){
  cin>>n;
  for(int i=1;i<=n;i++){
    int a,b;cin>>a>>b;
    cnt++;
    wk[cnt].v=a;wk[cnt].t=0;wk[cnt].id=i;
    cnt++;
    wk[cnt].v=b;wk[cnt].t=1;wk[cnt].id=i;
    ru[i].first=a;ru[i].second=b;
  }
  sort(wk+1,wk+1+cnt,cmp);
  b(1,1,cnt);
  ll ans=0;
  for(int i=cnt;i>=1;i--){
    mp[wk[i].v]=i;
    if(wk[i].t==1){
      asiend(1,i);
    }
    if(wk[i].t==0){
      int tk=wk[i].id;
      int rr=mp[ru[tk].second];
      ans=ans+qry(1,i,rr);
      asibeg(1,rr);
    }
  }
  cout<<ans<<'\n';
} 
int main(){
  
  int T;cin>>T;
  while(T--){
    init();
    solve();
  }
  
  return 0;
}

CF1313C2

是个比较典的两边和一块,用线段树二分一类的东西从前往后和从后往前处理出造高楼的总高度,然后枚举合并点统计答案即可。

从后往前处理有个小技巧就是把原来的数组翻一下就能当正向做了,不用再另写一个函数。

代码有一点长,但是是很好写的。

题解有比较高妙的单调栈做法。

code

Show me the code
#define psb push_back
#define mkp make_pair
#define ls p<<1
#define rs (p<<1)+1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int long long
ll read(){
  ll x=0,f=1;
  char c=getchar();
  while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
  while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
  return x*f;
}
const int N=5e5+5;
int num[N];
struct seg{
  int l;int r;
  ll v;
  ll lzt;
  ll maxv;
}t[N<<2];
void b(int p,int l,int r){
	t[p].maxv=t[p].v=0;
	t[p].lzt=0;
  t[p].l=l;t[p].r=r;
  if(l==r){
    t[p].maxv=t[p].v=0;
    return ;
  }
  int mid=l+r>>1;
  b(ls,l,mid);b(rs,mid+1,r);
  t[p].v=t[ls].v+t[rs].v;
  t[p].maxv=max(t[ls].maxv,t[rs].maxv);
  return ;
}
void pushdown(int p){
  if(!t[p].lzt)return ;
  int k=t[p].lzt;t[p].lzt=0;
  t[ls].lzt=k;
  t[ls].v=(t[ls].r-t[ls].l+1)*k;
  t[ls].maxv=k;
  t[rs].lzt=k;
  t[rs].v=(t[rs].r-t[rs].l+1)*k;
  t[rs].maxv=k;
  return ;
}
int qmax(int p,int l,int r,ll k){
  if(t[p].l==t[p].r)return t[p].l;
  if(l<=t[p].l&&t[p].r<=r){
    int mid=t[p].l+t[p].r>>1;
    pushdown(p);
    if(t[ls].maxv>k)return qmax(ls,l,r,k);
    if(t[rs].maxv>k)return qmax(rs,l,r,k);
    else return 0x3f3f3f3f;
  }
  pushdown(p);
  int mid=t[p].l+t[p].r>>1;
  int sub=0x3f3f3f3f;
  if(l<=mid)sub=min(sub,qmax(ls,l,r,k));
  if(mid<r) sub=min(sub,qmax(rs,l,r,k));
  return sub;
}
void asi(int p,int l,int r,ll k){
  if(l<=t[p].l&&t[p].r<=r){
    t[p].lzt=k;
    t[p].v=(t[p].r-t[p].l+1)*k;
    t[p].maxv=k;
    return ;
  }
  pushdown(p);
  int mid=t[p].l+t[p].r>>1;
  if(l<=mid)asi(ls,l,r,k);
  if(mid<r) asi(rs,l,r,k);
  t[p].v=t[ls].v+t[rs].v;
  t[p].maxv=max(t[ls].maxv,t[rs].maxv);
  return ;
}
ll qry(int p,int l,int r){
  if(l<=t[p].l&&t[p].r<=r){
    return t[p].v;
  }
  pushdown(p);
  int mid=t[p].l+t[p].r>>1;
  ll sub=0;
  if(l<=mid)sub+=qry(ls,l,r);
  if(mid<r) sub+=qry(rs,l,r);
  return sub; 
}
ll f[N],g[N];
int ans[N];
queue<int> ansl;
signed main(){
  
  int n;cin>>n;
  for(int i=1;i<=n;i++)cin>>num[i];
  b(1,1,n);
  for(int i=1;i<=n;i++){
    if(i==1){
      asi(1,1,1,num[1]);
      f[i]=num[1];
      continue;
    }
    int cmax=qmax(1,1,i,num[i]);
    if(cmax==0x3f3f3f3f)cmax=i;
    asi(1,cmax,i,num[i]);
    f[i]=qry(1,1,i);
  }
  reverse(num+1,num+1+n);
  b(1,1,n);
  for(int i=1;i<=n;i++){
    if(i==1){
      asi(1,1,1,num[1]);
      g[i]=num[1];
      continue;
    }
    int cmax=qmax(1,1,i,num[i]);
    if(cmax==0x3f3f3f3f)cmax=i;
    asi(1,cmax,i,num[i]);
    g[i]=qry(1,1,i);
  }
  reverse(g+1,g+1+n);
  reverse(num+1,num+1+n);
  ll ans=max(f[n],g[1]);
  int p=0;
  if(f[n]>g[1])p=n;
  else p=1;
  for(int i=1;i<n;i++){
    if(ans<f[i]+g[i+1]){
      if(num[i]>num[i+1])p=i;
      else p=i+1;
      ans=f[i]+g[i+1];
    }
  }
  if(p==1)p=0;
  b(1,1,n);
  for(int i=1;i<=p;i++){
    if(i==1){
      asi(1,1,1,num[1]);
      f[i]=num[1];
      continue;
    }
    int cmax=qmax(1,1,i,num[i]);
    if(cmax==0x3f3f3f3f)cmax=i;
    asi(1,cmax,i,num[i]);
  }
  for(int i=1;i<=p;i++){
    int k=qry(1,i,i);
    ansl.push(k);
  }
  reverse(num+1,num+1+n);
  b(1,1,n);
  stack<int> s;
  for(int i=1;i<=n-p;i++){
    if(i==1){
      asi(1,1,1,num[1]);
      g[i]=num[1];
      continue;
    }
    int cmax=qmax(1,1,i,num[i]);
    if(cmax==0x3f3f3f3f)cmax=i;
    asi(1,cmax,i,num[i]);
  }
  for(int i=1;i<=n-p;i++){
    int k=qry(1,i,i);
    s.push(k);
  }
  while(s.size()){
  	ansl.push(s.top());
  	s.pop();
	}
  while(ansl.size()){
    cout<<ansl.front()<<' ';
    ansl.pop();
  }

  return 0;
}

AGC032B

想到了建反图但是未遂。

首先有个结论:若一个无向图不连通,则这个图的反图一定连通。

证明是这样:考虑原来不连通的无向图中的两个点 \((u,v)\),若两点不在同一个连通块中,则反图必然会直接连接他们两点,保证了他们在反图中连通。

若两点在同一个连通块中,任取不在该连通块中的一点 \(t\),则反图中必然存在边 \((u,t),(v,t)\),因此他们在反图中连通。

综上可得所有点对均连通,也就是说反图是一个连通块。

考虑一个这样的图:这个图不连通,对于每一个点,它的点权加上它邻接的点的点权都相等。

于是这个图的反图就满足:这个图连通,对于每一个点,它邻接的点的点权和都相等。因为总的点权和是相等的。

于是这题就做完了,这种反图是非常好构造的。

code

Show me the code
#define psb push_back
#define mkp make_pair
#define ls p<<1
#define rs (p<<1)+1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
  ll x=0,f=1;
  char c=getchar();
  while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
  while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
  return x*f;
}
const int N=150;
bool ink[N][N];
int main(){
  
  int n;cin>>n;
  if(n==3){
    cout<<2<<'\n';
    cout<<1<<' '<<3<<'\n';
    cout<<2<<' '<<3<<'\n';
    return 0;
  }
  if(n%2==0){
    for(int i=1;i+i<=n;i++){
      ink[i][n-i+1]=1;
      ink[n-i+1][i]=1;
    }
    vector<pair<int,int> >p;
    for(int i=1;i<=n;i++){
      for(int j=i;j<=n;j++){
        if(i==j)continue;
        if(!ink[i][j])p.push_back(mkp(i,j));
      }
    }
    cout<<p.size()<<'\n';
    for(pair<int,int> c:p){
      cout<<c.first<<' '<<c.second<<'\n';
    }
    return 0;
  }
  if(n%2==1){
    for(int i=1;i<=n/2;i++){
      ink[i][n-i]=1;
      ink[n-i][i]=1;
    }
    vector<pair<int,int> >p;
    for(int i=1;i<=n;i++){
      for(int j=i;j<=n;j++){
        if(i==j)continue;
        if(!ink[i][j])p.push_back(mkp(i,j));
      }
    }
    cout<<p.size()<<'\n';
    for(pair<int,int> c:p){
      cout<<c.first<<' '<<c.second<<'\n';
    }
    return 0;
  }
  
  return 0;
}
posted @ 2025-08-10 17:43  hm2ns  阅读(6)  评论(0)    收藏  举报