The 2022 ICPC Asia Shenyang Regional Contest(DCLFA)

The 2022 ICPC Asia Shenyang Regional Contest(DCLFA)

D. DRX vs. T1

签到题 , 甚至题面不用看. 直接看输入输出就行了.


C. Clamped Sequence

思维题 , 思路比较简单.


首先我们注意到 : 该操作不会影响相对大小 , 故绝对值符号可以去掉 , 求出一个权值 .

然后观察数据范围 , 似乎是一个\(O(N^2)\)的题.

容易发现\([l,r]\)一定是整数 , 不妨设\(l\in [x,x+1),r\in[y,y+1)\) 完全等价于\(l=x,r=y\).

直接枚举边界显然会\(T\), 考虑寻找一些性质.

猜测两个端点只会在数组里面的数字产生 , 显然错误.

2 1
-10 10

这个例子让人猜测加一些权值为0的\(a_i\pm d\)的点 ,然后暴力枚举.然后就过了

void solve() {
  int n,d;cin>>n>>d;
  n*=3;
  vector<pair<int,int>> a(n+1);
  for (int i=2;i<=n;i+=3) cin>>a[i].first,a[i-1].first=a[i].first-d,a[i+1].first=a[i].first+d;
  for (int i=2;i+3<=n;i+=3) {
    if (a[i].first<a[i+3].first) {
      a[i].second--,a[i+3].second++;
    }else {
      a[i+3].second--,a[i].second++;
    }
  }
  sort(a.begin()+1,a.end());
  // for (int i=1;i<=n;++i) {
  //   cout<<a[i].first<<" "<<a[i].second<<endl;
  // }
  vector<int> p(n+1),s(n+2);
  vector<int> f(n+1);
  for (int i=1;i<=n;++i) p[i]=p[i-1]+a[i].second;
  for (int i=n;i;--i) s[i]=s[i+1]+a[i].second;
  for (int i=1;i<=n;++i) f[i]=f[i-1]+a[i].first*a[i].second;
  int ans=0;
  for (int i=1;i<=n;++i) {
    for (int j=i;j<=n;++j) {
      if (a[j].first-a[i].first<=d) {
        ans=max(ans,p[i-1]*a[i].first+s[j+1]*a[j].first+f[j]-f[i-1]);
      }
    }
  }
  cout<<ans<<"\n";
}

下面给出证明:

引理1:一定有个边界是数组中的元素.

证明:\(x[y]z\) , 其中\(x,y,z\)是权值和. 若\(x>0\),那么我们将左边界移动至右侧的元素显然更优. 同理\(y<0\)可以将右边界想左移.

现在有\(x\le 0,y\ge0\),考虑整体移动区间, 在边界不跨越数字时,权值不会改变 , 因此一定会向一个固定的方向移动至有一个边界与数字重合.

引理2:区间越大越好.

证明:假设\(r\) 定, 那么若\(a<l\),且其权重为负我们显然希望\(l\)变小, 区间变大, 否则其权重为正 , 那么每个\(+1\) 的权重其相邻的位置一定有个\(-1\)的小于\(l\) 的 , 相互抵消. \(l\) 定 同理.

因此我们插入的点已经覆盖了所有可能性.同时这还告诉我们区间长度一定是\(d\),因此这样写也完全没问题.

if (a[j].first-a[i].first==d) {
    ans=max(ans,p[i-1]*a[i].first+s[j+1]*a[j].first+f[j]-f[i-1]);
}

L. Tavern Chess

模拟题 , 英语题 (没给样例解释有点恶心 , 容易读假题)


数据范围很小 , 直接模拟即可 , 规则有点复杂 , 不读假题的话还是很简单的

#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e6 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;
struct M {
  int hp,atk;
  int c;
};
long double pa,pb,pt;
int n,m;
vector<M> a,b;
//t=0 alice

void work(bool t,long double p) {
  int ra=0,rb=0,ca=n+1,cb=m+1,ma=iinf,mb=iinf;
  for (int j=1;j<=n;++j) {
    if (a[j].hp>0) {
      ra++;
      if (a[j].c<ma) {
        ca=j;
        ma=a[j].c;
      }
    }
  }
  for (int j=1;j<=m;++j) {
    if (b[j].hp>0) {
      rb++;
      if (b[j].c<mb) {
        cb=j;
        mb=b[j].c;
      }
    }
  }
  if (ra==0 or rb==0) {
    if (ra==0 and rb==0)pt+=p;
    else if (ra==0)pb+=p;
    else if (rb==0)pa+=p;
    return ;
  }
  if (t==0) {
    long double pp=1./rb;
    // cerr<<pp<<endl;
    for (int i=1;i<=m;++i) {
      if (b[i].hp<=0) continue;
      a[ca].hp-=b[i].atk;
      b[i].hp-=a[ca].atk;
      a[ca].c++;
      work(t^1,p*pp);
      a[ca].c--;
      a[ca].hp+=b[i].atk;
      b[i].hp+=a[ca].atk;
    }
  }else {
    long double pp=1./ra;
    for (int i=1;i<=n;++i) {
      if (a[i].hp<=0) continue;
      b[cb].hp-=a[i].atk;
      a[i].hp-=b[cb].atk;
      b[cb].c++;
      work(t^1,p*pp);
      b[cb].c--;
      b[cb].hp+=a[i].atk;
      a[i].hp+=b[cb].atk;
    }
  }
}

void solve() {
  cin>>n>>m;
  a.resize(n+1),b.resize(m+1);
  for (int i=1;i<=n;++i) cin>>a[i].hp,a[i].atk=a[i].hp;
  for (int i=1;i<=m;++i) cin>>b[i].hp,b[i].atk=b[i].hp;
  if (n>m) {
    work(0,1);
  }else if (n<m) {
    work(1,1);
  }else {
    work(0,0.5);
    work(1,0.5);
  }
  cout<<fixed<<setprecision(12)<<pa<<"\n";
  cout<<fixed<<setprecision(12)<<pb<<"\n";
  cout<<fixed<<setprecision(12)<<pt<<"\n";
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
  freopen("C:\\ACM\\code\\test.in", "r", stdin);
  freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endif
  int Case = 1;
  // cin >> Case;
  while (Case--) solve();
  return 0;
}


F. Half Mixed

构造题 .


直接做感觉很困难 , 观察到\(2\times 3\)以下构造可行

0 1 0
0 1 0

然后发现

0 1 0
0 1 0
0 1 0

也可行.

容易想到只用构造一行,然后直接复制过去.

实际上若一行上可以平分 , 假定 选择了行, 可以压缩成一行, 正确性比较显然.

但一行什么时候有解呢.显然需要总的块数为偶数 . 即列数为\(4k\)或者\(4k+3\).

若原始列数不行,可以考虑交换行列,若都不行,显然无解.否则一定有解.

注意要换回去

#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e6 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;

bool check(int x) {
  if (x%4==1 or x%4==2) return false;
  return true;
}



void solve() {
  int n,m;cin>>n>>m;
  if (!check(n) and !check(m)) {
    cout<<"No\n";
    return ;
  }
  cout<<"Yes\n";
  bool T=false;
  if (!check(m)) swap(n,m),T=true;
  vector<int> ans(m+1);
  int sum=(m*(m+1)/4)-m,cnt=1;
  for (int now=2;now<=m;++now) {
    if (cnt<=sum) {
      sum-=cnt;
      ans[now]=ans[now-1];
      cnt++;
    }else {
      ans[now]=ans[now-1]^1;
      cnt=1;
    }
  }
  vector<vector<int>> a(n+1,vector<int>(m+1));
  for (int i=1;i<=n;++i) a[i]=ans;
  if (T) {
    for (int j=1;j<=m;++j) {
      for (int i=1;i<=n;++i) {
        cout<<a[i][j]<<" ";
      }
      cout<<"\n";
    }
  }else {
    for (int i=1;i<=n;++i) {
      for (int j=1;j<=m;++j) {
        cout<<a[i][j]<<" ";
      }
      cout<<"\n";
    }
  }
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
  freopen("C:\\ACM\\code\\test.in", "r", stdin);
  // freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endif
  int Case = 1;
  cin >> Case;
  while (Case--) solve();
  return 0;
}

A. Absolute Difference

可以先考虑2个区间的情况, 有以下几种情况

设A选出\([a,b]\)的概率是\(P_a\) ,B选出\([c,d]\)的概率是\(P_b\)

设定义一个区间\([l,r]\)的mid为\(mid=\frac{l+r}{2}\)

  • 若不相交 , \(E(|Y-X|)=|EY-EX|=P_aP_b|midA-midB|\)

  • 存在相交部分, 可以转换为不相交和完全重合.

  • 完全重合\(E(|Y-X|)=P_aP_b\frac{r-l}{3}\)

现在显然有个\(O(mn)\)的算法 , 下面考虑优化. 显然可以尝试前缀和优化. 对于完全重合的部分只有\(O(n+m)\),不用管它.

不相交的部分, 我们按\(mid\)排序 , 就可以去掉绝对值符号.

具体地

\[\sum_{i}P_aP_{b_i}midA-P_aP_{b_i}midB_i=P_amidA\sum_{i}P_{b_i}-P_a\sum_{i}P_{b_i}midB_i \]

存下

\[\sum_{i}P_{b_i} \qquad \sum_{i}P_{b_i}midB_i \]

即可计算.

#include <bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;
const int mod = 998244353;
const int N = 1e6 + 9;
const int iinf = 1e9;
const i64 linf = 1e18;
struct node {
  int x,f;
  bool operator<(const node& u)const {
    if (x!=u.x) return x<u.x;
    return f<u.f;
  }
};

void solve() {
  int n,m;cin>>n>>m;
  vector<node> p(1);
  int lena=0,lenb=0;
  for (int i=1;i<=n;++i) {
    int l,r;cin>>l>>r;
    lena+=r-l;
    p.push_back({l,0});
    p.push_back({r,0});
  }
  for (int i=1;i<=m;++i) {
    int l,r;cin>>l>>r;
    lenb+=r-l;
    p.push_back({l,1});
    p.push_back({r,1});
  }
  sort(p.begin()+1,p.end());
  long double pa=0,sa=0,pb=0,sb=0,ans=0;
  int a=0,b=0;
  auto PA=[&](int len)->long double  {
    if (lena==0) return 1.L/n;
    return 1.L*len/lena;
  };
  auto PB=[&](int len)->long double {
    if (lenb==0) return 1.L/m;
    return 1.L*len/lenb;
  };
  for (int i=1;i+1<p.size();++i) {
    auto [x,f]=p[i];
    auto [nx,nf]=p[i+1];
    int len=nx-x;
    long double mid=(x+nx)/2.L;
    if (f) b^=1;
    else a^=1;
    if (a and b) ans+=1.L/3.L*len*PA(len)*PB(len);
    if (a) ans+=PA(len)*(mid*pb-sb);
    if (b) ans+=PB(len)*(mid*pa-sa);
    if (a) pa+=PA(len),sa+=PA(len)*mid;
    if (b) pb+=PB(len),sb+=PB(len)*mid;
  }
  cout<<fixed<<setprecision(14)<<ans;
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
#ifndef ONLINE_JUDGE
  freopen("C:\\ACM\\code\\test.in", "r", stdin);
  // freopen("C:\\ACM\\code\\test2.in", "r", stdin);
#endif
  int Case = 1;
  // cin >> Case;
  while (Case--) solve();
  return 0;
}
posted @ 2025-04-20 13:43  _lull  阅读(14)  评论(0)    收藏  举报