省选模拟 2

T 营模拟,所以四道题,梦回 NOIP 前模拟赛。

A 石子游戏(stone)

点击查看 天天放博弈论,博弈论是你爹吗?

先打表,发现 B 赢麻了,A 赢的时候几乎全是 1,前面全是 1,后面带了一个 2 的时候 A 也会赢,由于 A 每次拿一堆,B 每次拿两堆,直接猜答案与 12,和其他数的个数有关。
考虑用 DP 打表,fi,j,k,0/1 表示有 i1j2k 个其他,发现可以自己转移自己,但是我们已经钦定了 答案与 1,2,和其他数的个数有关,所以其他数不能变成其他数,只能变成 1,2,所以直接不管,写完后发现这个是对的,并且能过题。
那如果 n 很大呢,再次观察题意和打的表,首先有一个很简单的情况,如果全是 1,那么当 n0(mod3) 时,Alice 必败,考虑在全是 1 的基础上再加一个不为 1 的数 x,Alice 可以选择拿走 x 或者把 x 变成 1,两种总有一种会赢,得出另一个结论,只有一个不为 1 的数时,Alice 先手必胜。
考虑现在有 k 个数大于 1
考虑 Bob 的策略,他一定要尽量避免 k=1
如果 k=2,那么 Bob 可以增加 0,1,21,必胜。如果 k=1,如果这个数大于 2,那么 Bob 可以增加 0,1,11,必胜,如果这个数是 2,那 Bob 只能增加 0,11,如果同时 n12(mod3),那么 Bob 必败,所以这种情况要求一堆 1,一个 2,一个 xn20(mod3) Alice 才能必胜,他可以增加 0,11
其他情况 Bob 都可以及时调整。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,b[105];
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
int T=read();while(T--){
int a=0;
n=read();for(int i=1;i<=n;++i)b[i]=read(),a+=b[i]==1;
if(a==n){if(n%3)std::cout<<"Win\n";else std::cout<<"Lose\n";continue;}
if(a==n-1){std::cout<<"Win\n";continue;}
if(a==n-2){
bool pd=0;
for(int i=1;i<=n;++i)if(b[i]==2)pd=1;
if(pd&&n%3!=2)std::cout<<"Win\n";
else std::cout<<"Lose\n";
continue;
}
std::cout<<"Lose\n";
}
}

B 树上字符串(treestr)

不难想到预处理 i,j 区间在树上的信息,这个复杂度肯定是 O(n|S|2) 了,可以想到倍增或者树剖来拼接信息,但是这样会多一个 log,考虑还是用树上前缀和来做这个,fi,j,k 表示从根到 iS[j,k] 出现的次数,发现拼接需要容斥,类似于一个退背包的东西,直接从小到大退就行了。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
const int N=1e5+10,mod=998244353;
inline void W(int &x,int y){x=(x+y)%mod;}
int n,q,d[N][32][32],st[20][N],len,fu[N],tmp1[N],tmp2[N],dfn[N],dn;
char S[N],T[N];
std::vector<int> e[N],v[N];
inline int get(int x,int y){return dfn[x]<dfn[y]?x:y;}
inline void dfs(int x,int fa){
fu[x]=fa;
for(int i=1;i<=len;++i)for(int j=1;j<=len;++j)d[x][i][j]=d[fa][i][j];
for(int y:v[x]){
W(d[x][y][y],1);
for(int i=y+1;d[fa][i][y+1];++i)W(d[x][i][y],d[fa][i][y+1]);
for(int i=y-1;d[fa][i][y-1];--i)W(d[x][i][y],d[fa][i][y-1]);
}
st[0][dfn[x]=++dn]=fa;for(int v:e[x])if(v^fa)dfs(v,x);
if(x==1)for(int i=1;i<=std::__lg(n);++i)for(int j=1;j+(1<<i)-1<=n;++j)st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
inline int LCA(int u,int v){
if(u==v)return u;
if((u=dfn[u])>(v=dfn[v]))std::swap(u,v);
int d=std::__lg(v-u++);return get(st[d][u],st[d][v-(1<<d)+1]);
}
inline int query(int u,int v){
int lca=LCA(u,v),f=fu[lca],res=0;
for(int i=1;i<=len;++i)tmp1[i]=d[u][i][1],tmp2[i]=d[v][i][len];
tmp1[0]=1;tmp2[len+1]=1;
for(int i=1;i<=len;++i)for(int j=i;j;--j)W(tmp1[i],-1ll*tmp1[j-1]*d[f][i][j]%mod);
f=lca;for(int i=len;i;--i)for(int j=i;j<=len;++j)W(tmp2[i],-1ll*tmp2[j+1]*d[f][i][j]%mod);
for(int i=0;i<=len;++i)W(res,1ll*tmp1[i]*tmp2[i+1]%mod);return (res+mod)%mod;
}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
std::cin>>n>>q;for(int i=1;i<n;++i){int u,v;std::cin>>u>>v;e[u].eb(v);e[v].eb(u);}
std::cin>>T+1>>S+1;len=strlen(S+1);
for(int i=1;i<=n;++i)for(int j=1;j<=len;++j)if(T[i]==S[j])v[i].eb(j);
dfs(1,0);for(int i=1;i<=q;++i){int u,v;std::cin>>u>>v;std::cout<<query(u,v)<<"\n";}
}

C 区间划分(divid)

注意到这个好区间的数量并不多,最多只有 nlogn 个,如果能找到所有好区间,就能直接用这些区间来 DP 了,如果一个区间的长度为 len,其中最大的一个数为 2a,那么 x[a,a+loglen],所以可以直接暴力分治来找到所有区间,每次找到区间中最大值的位置 mid,然后以小的区间中的位置为端点来找另一个区间的端点,用哈希存前缀和即可,需要一个巨大无比的模数。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
const int N=6e5+10,mod=1e9+7,M=1e6+30;
const ll P=100000000000000003;
inline void W(int &x,int y){x=(x+y)%mod;}
int n,a[N],st[20][N],f[N],lg[N];
ll p[M],s[N];
std::unordered_map<ll,int> mp;
inline int get(int x,int y){return a[x]<a[y]?y:x;}
inline int Get(int l,int r){int d=lg[r-l+1];return get(st[d][l],st[d][r-(1<<d)+1]);}
inline void sol(int l,int r){
if(l>r)return ;
if(l==r)return W(f[l],f[l-1]),void();
int mid=Get(l,r);sol(l,mid-1);
int L=a[mid],R=L+lg[r-l+1];
if(mid-l+1<=r-mid){
for(int i=l;i<=mid;++i){
for(int ai=L;ai<=R;++ai){
ll ne=(s[i-1]+p[ai])%P;
if(mp.find(ne)!=mp.end()){
int wc=mp[ne];
if(wc>=mid&&wc<=r)W(f[wc],f[i-1]);
}
}
}
}else{
for(int i=mid;i<=r;++i){
for(int ai=L;ai<=R;++ai){
ll ne=(s[i]-p[ai]+P)%P;
if(mp.find(ne)!=mp.end()){
int wc=mp[ne];
if(wc>=l-1&&wc<mid)W(f[i],f[wc]);
}
}
}
}
sol(mid+1,r);
}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read();f[0]=1;lg[0]=-1;
for(int i=1;i<=n;++i)lg[i]=lg[i>>1]+1;
p[0]=1;for(int i=1;i<=M-10;++i)p[i]=p[i-1]*2%P;
mp[0]=0;for(int i=1;i<=n;++i)a[i]=read(),s[i]=(s[i-1]+p[a[i]])%P,st[0][i]=i,mp[s[i]]=i;
for(int i=1;i<=std::__lg(n);++i)for(int j=1;j+(1<<i)-1<=n;++j)st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
sol(1,n);std::cout<<f[n]<<'\n';
}

D 机器(machine)

经典水龙头接水问题,一个人的贡献是多少个人(包括自己)在等他,那么最小代价的方案就是前 K 大有一个人等,第二 K 大有两个人等,以此类推。
题目没要求在线,可以先把所有人都拿出来,每次操作就相当于停用一个人,启用一个人。把所有人从大到小排序后放到一个 m 行乘 K 列的矩形里,第 i 行的系数是 i,假设两个人的位置分别是 l,r(l<r),那么他们之间的人都会往前平移一个,只有每行第一个人的系数才会变,(l>r) 同理。
如果 m 比较小,可以每行维护,如果 K 比较小,可以每列维护,对 K 根号分治后做到 O(nnlogn),过不了。
考虑分块,di,j 表示第 i 个块中模 Kj 的值的和,注意到每个块中只有 B 个有效位置,所以可以做一个偏移的东西,再记一个 si 表示第 i 个块中有效值的数量即可。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void write(__int128 x){if(x>=10)write(x/10);putchar(x%10+'0');}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
const int N=6e5+10,LEN=800;
int n,K,A[N],Q,tot,num[LEN],match[N],len,ID[N];
ll s[LEN][LEN];
__int128 ans;
bool vis[N];
struct NODE{int x,id;}a[N],b[N];
struct QU{int l,r;}q[N];
inline ll c(int rk,int pos){return 1ll*((rk-1)/K+1)*a[pos].x;}
inline void pro(int id){
int l=(id-1)*len+1,r=std::min(tot,l+len-1);
for(int i=0;i<=len;++i)s[id][i]=0;num[id]=0;
for(int i=l,now=0;i<=r;++i)if(vis[i])s[id][now++%K]+=a[i].x,num[id]++;
}
inline ll get(int id,int x,int pre){int zc=((x-pre)%K+K)%K;if(zc>=len)return 0;return s[id][zc];}
inline void work(int l,int r){
int lid=ID[l],rid=ID[r];
if(l<r){
int rk=0;for(int i=1;i<lid;++i)rk+=num[i];
for(int i=l;ID[i]==lid;--i)rk+=vis[i];ans-=c(rk,l);
vis[l]=0,vis[r]=1;
if(lid==rid){
for(int i=l+1;i<r;++i){rk+=vis[i];if(vis[i])ans-=c(rk,i)-c(rk-1,i);}ans+=c(rk,r);
pro(lid);
}else{
for(int i=l+1;ID[i]==lid;++i){rk+=vis[i];if(vis[i])ans-=c(rk,i)-c(rk-1,i);}
for(int i=lid+1;i<rid;++i)ans-=get(i,0,rk),rk+=num[i];
for(int i=(rid-1)*len+1;i<r;++i){
rk+=vis[i];if(vis[i])ans-=c(rk,i)-c(rk-1,i);
}ans+=c(rk,r);pro(lid);pro(rid);
}
}else{
std::swap(l,r);std::swap(lid,rid);
int rk=0;for(int i=1;i<lid;++i)rk+=num[i];
vis[l]=1;vis[r]=0;for(int i=l;ID[i]==lid;--i)rk+=vis[i];
ans+=c(rk,l);
if(lid==rid){
for(int i=l+1;i<r;++i){rk+=vis[i];if(vis[i])ans+=c(rk,i)-c(rk-1,i);}ans-=c(rk,r);
pro(lid);
}else{
for(int i=l+1;ID[i]==lid;++i){rk+=vis[i];if(vis[i])ans+=c(rk,i)-c(rk-1,i);}
for(int i=lid+1;i<rid;++i)ans+=get(i,K-1,rk-1),rk+=num[i];
for(int i=(rid-1)*len+1;i<r;++i){
rk+=vis[i];if(vis[i])ans+=c(rk,i)-c(rk-1,i);
}ans-=c(rk,r);pro(lid),pro(rid);
}
}
write(ans);putchar('\n');
}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read(),K=read();for(int i=1;i<=n;++i)A[i]=read(),a[++tot]={A[i],i},b[i]={A[i],i};
int Q=read();for(int i=1;i<=Q;++i){
int p=read(),x=read();q[i]={b[p].id,n+i};b[p]={x,n+i};a[++tot]={x,n+i};
}std::sort(a+1,a+tot+1,[](NODE A,NODE B){return A.x>B.x;});
len=std::sqrt(tot);
std::sort(A+1,A+n+1,[](int a,int b){return a>b;});for(int i=1;i<=n;++i)ans+=1ll*((i-1)/K+1)*A[i];
for(int i=1;i<=tot;++i)match[a[i].id]=i,ID[i]=(i-1)/len+1;
for(int i=1;i<=n;++i)vis[match[i]]=1;
for(int i=1;i<=Q;++i)q[i].l=match[q[i].l],q[i].r=match[q[i].r];
for(int i=1;i<=tot;i+=len)pro(ID[i]);
for(int i=1;i<=Q;++i)work(q[i].l,q[i].r);
}

总结

T2 不会大傻逼,赛时感觉容斥不了就把正解弃了。T1 博弈论不会大傻逼。

posted @   Ishar-zdl  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示