NOIP2024加赛6

一签三计数,罚坐了。

草莓

简单贪心,随便贪就过了。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
// FILE *InFile = stdin,*OutFile = stdout;
FILE *InFile = freopen("guiltiness.in","r",stdin),*OutFile = freopen("guiltiness.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 2e5 + 10;
int n,m,a[N],b[N];
inline void solve(){
cin>>n>>m;rep(i,1,n-1,1) cin>>a[i];rep(i,1,m-1,1) cin>>b[i];
sort(a+1,a+n,greater<int>());sort(b+1,b+m,greater<int>());
ll ans = 0;
int na = 1,nb = 1,ta = 0,tb = 0;
while(na < n && nb < m){
if(a[na] >= b[nb]) ans += 1ll*(tb+1)*a[na],na++,ta++;
else ans += 1ll*(ta+1)*b[nb],nb++,tb++;
}
while(na < n) ans += 1ll*(tb+1)*a[na],na++,ta++;
while(nb < m) ans += 1ll*(ta+1)*b[nb],nb++,tb++;
cout<<ans;
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
solve();
}

三色

弱化版:[ARC074E] RGB Sequence

先考虑O(n3)怎么做。

fi,j,k表示上一个与ai颜色不同的位置为j,上一个与ai,aj颜色都不同的位置为k时的方案数。

发现一定有 i>j>k(当i=1时除外,此时j=k=0),且 f1,0,0=3

考虑如何从 i 转移到 i+1

  1. ai+1=ai,那么fi+1,j,k+=fi,j,k
  2. ai+1=aj,那么fi+1,i,k+=fi,j,k
  3. ai+1=ak,那么fi+1,i,j+=fi,j,k

考虑如何剔除不合法状态,其实就是将不合法的状态置为 0,将一个形如 (l,r,x) 的限制挂在 r 上,对x进行分讨。

  1. x=1,那么j<l
  2. x=2,那么k<l,jl
  3. x=3,那么lk<j

保留这些合法状态,其他的置为 0 即可。

点此查看代码
//[ARC074E] RGB Sequence
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
FILE *InFile = stdin,*OutFile = stdout;
// FILE *InFile = freopen("color.in","r",stdin),*OutFile = freopen("color.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 5e2 + 10,M = 1e6 + 10,mod = 1e9 + 7;
struct limit{int l,x;};
vector<limit> lim[N];
int n,m,f[N][N][N];
inline void solve(){
cin>>n>>m;rep(i,1,n,1) vector<limit> ().swap(lim[i]);
rep(i,1,m,1){int l,r,x;cin>>l>>r>>x;lim[r].push_back({l,x});}
rep(i,0,n,1) rep(j,0,n,1) rep(k,0,n,1) f[i][j][k] = 0;
f[1][0][0] = 3;
rep(i,1,n,1){
for(auto [l,x]:lim[i]) rep(j,0,i-1,1){
int lmt = j-(!!j);
rep(k,0,lmt,1){
if(x == 1 && l <= j) f[i][j][k] = 0;
if(x == 2 && (l <= k || j < l)) f[i][j][k] = 0;
if(x == 3 && k < l) f[i][j][k] = 0;
}
}
if(i == n) break;
rep(j,0,i-1,1){
int lmt = j - (!!j);
rep(k,0,lmt,1){
if(!f[i][j][k]) continue;
f[i+1][j][k] = (f[i+1][j][k] + f[i][j][k])%mod;
f[i+1][i][k] = (f[i+1][i][k] + f[i][j][k])%mod;
f[i+1][i][j] = (f[i+1][i][j] + f[i][j][k])%mod;
}
}
}
int ans = 0;
rep(j,0,n-1,1){
int lmt = j - (!!j);
rep(k,0,lmt,1) ans = (ans + f[n][j][k])%mod;
}
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
int T = 1;while(T--) solve();
}

考虑正解,将j,k看做横纵两维,那么转移时要做的操作就是保留一个矩阵,然后将其他位置置为0

发现转移时有三种,(j,k),(i,k),(i,j),第一种不用管,滚动数组自动继承,后两种发现都是i,就相当于加入一行。

si=j=1nfnow,i,j+fnow,j,i,因为矩阵是对称的,这样可以做到O(n)加入一行。

具体的,用ai记录fnow,i这一行中没有删去的,用bi记录fnow,,j这一列没有删去的位置,然后用p,q再分别映射回原位置即可。

时间复杂度O(T(n2+m))

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
// FILE *InFile = stdin,*OutFile = stdout;
FILE *InFile = freopen("color.in","r",stdin),*OutFile = freopen("color.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
#define eb emplace_back
#define int long long
const int N = 5e3 + 10,M = 1e6 + 10,mod = 1e9 + 7;
struct limit{int l,x;};
vector<limit> lim[N];
int n,m,s[N],t[N];
vector<int> f,p,q,a[N],b[N];
inline void insert(int x,int y,int val){
if(!val) return;
int sz = f.size();f.eb(val),p.eb(x),q.eb(y);
a[x].eb(sz);b[y].eb(sz);s[x] = (s[x] + val + mod)%mod,s[y] = (s[y] + val + mod) % mod;
}
inline void solve(){
cin>>n>>m;
rep(i,1,m,1){int l,r,x;cin>>l>>r>>x;lim[r].push_back({l,x});}
insert(0,0,1);
rep(i,0,n,1){
int pl = 0,pr = 1e9,ql = 0,qr = 1e9;
for(auto [j,x]:lim[i]){
if(x == 1) pr = min(pr,j-1);
if(x == 2) pl = max(pl,j),qr = min(qr,j-1);
if(x == 3) ql = max(ql,j);
}
rep(j,0,i,1){
if(j >= pl && j <= pr) continue;
for(int x:a[j]) s[p[x]] = ((s[p[x]] - f[x])%mod + mod)%mod,s[q[x]] = ((s[q[x]] - f[x])%mod + mod)%mod,f[x] = 0;
vector<int> ().swap(a[j]);
}
rep(j,0,i,1){
if(j >= ql && j <= qr) continue;
for(int x:b[j]) s[p[x]] = ((s[p[x]] - f[x])%mod + mod)%mod,s[q[x]] = ((s[q[x]] - f[x])%mod + mod)%mod,f[x] = 0;
vector<int> ().swap(b[j]);
}
if(i < n){
rep(j,0,i,1) t[j] = s[j];
rep(j,0,i,1) insert(i,j,t[j]);
}
}
int ans = 0;
rep(i,0,n,1){
ans = (ans + s[i] + mod)%mod,s[i] = 0;
vector<int> ().swap(a[i]);
vector<int> ().swap(b[i]);
vector<limit> ().swap(lim[i]);
}
vector<int> ().swap(f);
vector<int> ().swap(p);
vector<int> ().swap(q);
cout<<ans*500000004ll%mod<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
int T;cin>>T;while(T--) solve();
}

博弈

假设最后留下的三个数分别为a,b,c,容易发现三个数的真实值并无影响,不妨设abc

分三种情况讨论。

  1. 三个值都相同,即a=b=c。那么先手直接输。

  2. 三个值都不相同,即a<b<c,此时先手必胜。

    证明:如果b=a+c2,则先手可以直接操作a,cb,则先手必胜。

    考虑b<a+c2b>a+c2,发现这两种情况是等价的,此处只考虑b<a+c2

    将其移动,假设a移动后的点为ac移动后的点为c,由于ab时无影响,不考虑,考虑a=b的。

    假如后手可以移动使得自己为处于必胜态,假设此时a的对应点为ac的对应点为c,那么先手就可以直接移动到a,c,使得后手无法处于必胜态。

    QED.

  3. 三个值中只有两个相同,发现a=b<ca<b=c等价,不妨设a=b<c。此时当且仅当lowbit(ca)为2的偶数次幂时先手必胜。

    证明:先手必胜时当且仅当(ca)mod2=0,否则就会使得后者存在a<b<c的状态使得后手必胜。

    假设fn表示ca=n时先手是否必胜,有fn=!fn2。此时可以发现当且仅当n的最后一位1为偶数位时,fn=true

然后分别求a<b<c时的值和a=b<c时的答案即可。求a<b<c时直接容斥即可,求a=b<c的有两种做法,log2nmap做法:直接对于每一位开map;logn的trie树上dp做法,具体的,考虑将偶数位的加上,奇数位的减去即可。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
// FILE *InFile = stdin,*OutFile = stdout;
FILE *InFile = freopen("game.in","r",stdin),*OutFile = freopen("game.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 5e5 + 10;
struct Trie{
int tree[N*60][2],tot,siz[N*60];
inline void insert(ll x){
siz[0]++;int p = 0;
rep(i,0,63,1){
int k = (x>>i)&1ll;
if(!tree[p][k]) tree[p][k] = ++tot,siz[tot] = 0;
p = tree[p][k];siz[p]++;
}
}
inline ll solve(ll x){
ll res = 0;
int p = 0,dep = 0;
rep(i,0,63,1){
int k = (x>>i)&1ll;
if(!tree[p][k]) break;
p = tree[p][k];dep++;
res += ((dep&1)?1:-1)*siz[p];
}
return res;
}
inline void clear(){rep(i,0,tot,1) tree[i][0] = tree[i][1] = 0;tot = 0;siz[0] = 0;}
}T;
int n,ct[N],tot;ll a[N],w[N];
inline ll C(int x){return x<2?0ll:1ll*x*(x-1)/2;}
inline void solve(){
cin>>n;rep(i,1,n,1) cin>>a[i];
sort(a+1,a+1+n);tot = 0;
rep(i,1,n,1){
int ed = i;
while(ed < n && a[ed + 1] == a[ed]) ed++;
ct[++tot] = ed-i+1,w[tot] = a[i];i = ed;
}
ll sum1 = 0,sum2 = 0,ans = 0;
rep(i,1,tot,1){
ll c = C(sum2) - sum1;ans += c*ct[i];
sum2 += ct[i];sum1 += C(ct[i]);
}
T.clear();rep(i,1,n,1) T.insert(a[i]);
rep(i,1,tot,1){
if(ct[i] < 2) continue;
ll c = C(ct[i]);
ans += c*T.solve(w[i]);
}
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
int T;cin>>T;while(T--) solve();
}

后缀数组

60pts:

用FHQ维护那m个修改操作,时间复杂度 mlogn。实现是参考文艺平衡树。然后统计所有 rk[a[i]+1]<rk[a[i+1]+1]的数量,答案是2k

没写。

p

image

posted @   CuFeO4  阅读(43)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示