【考试总结】2022-05-30
卡片
对每张牌计算最后它是正面朝上还是反面朝上
找到 区间中最后一次出现元素的出现位置,那么在这里这张牌必然是 朝上
剩下的工作就是后缀查有几个元素
Code Display
const int N=2e5+10;
int n,K,A[N],B[N],t[N];
int lsh[N<<2],m;
const int M=N*30;
int ls[M],rs[M],rt[N],tot,sum[M];
inline void insert(int &p,int pre,int l,int r,int v){
p=++tot; sum[p]=sum[pre]+1; ls[p]=ls[pre]; rs[p]=rs[pre];
if(l==r) return ;
int mid=(l+r)>>1;
if(v<=mid) insert(ls[p],ls[pre],l,mid,v);
else insert(rs[p],rs[pre],mid+1,r,v);
}
inline bool query(int p,int l,int r,int st,int ed){
if(!p) return 0;
if(st<=l&&r<=ed) return 1;
int mid=(l+r)>>1;
if(st<=mid&&query(ls[p],l,mid,st,ed)) return 1;
return ed>mid&&query(rs[p],mid+1,r,st,ed);
}
inline int ask(int p,int l,int r,int st,int ed){
if(!p) return 0;
if(st<=l&&r<=ed) return sum[p];
int mid=(l+r)>>1,res=0;
if(st<=mid) res+=ask(ls[p],l,mid,st,ed);
if(ed>mid) res+=ask(rs[p],mid+1,r,st,ed);
return res;
}
signed main(){
freopen("card.in","r",stdin); freopen("card.out","w",stdout);
n=read(); K=read();
ll ans=0;
for(int i=1;i<=n;++i){
A[i]=read(),B[i]=read();
if(A[i]==B[i]) ans+=A[i],--i,--n;
}
rep(i,1,n) lsh[++m]=A[i],lsh[++m]=B[i];
for(int i=1;i<=K;++i) t[i]=read(),lsh[++m]=t[i];
sort(lsh+1,lsh+m+1);
m=unique(lsh+1,lsh+m+1)-lsh-1;
for(int i=K;i>=1;--i){
t[i]=lower_bound(lsh+1,lsh+m+1,t[i])-lsh;
insert(rt[i],rt[i+1],1,m,t[i]);
}
for(int i=1;i<=n;++i){
int l=1,r=K,pos=K+1;
A[i]=lower_bound(lsh+1,lsh+m+1,A[i])-lsh;
B[i]=lower_bound(lsh+1,lsh+m+1,B[i])-lsh;
while(l<=r){
int mid=(l+r)>>1;
if(query(rt[mid],1,m,min(A[i],B[i]),max(A[i],B[i])-1)) l=mid+1;
else pos=mid,r=mid-1;
}
int tim=ask(rt[pos],1,m,max(A[i],B[i]),m);
if(pos==1){
if(tim&1) ans+=lsh[B[i]];
else ans+=lsh[A[i]];
}else{
if(tim&1) ans+=min(lsh[A[i]],lsh[B[i]]);
else ans+=max(lsh[A[i]],lsh[B[i]]);
}
}
print(ans);
return 0;
}
菜
让非最大值元素之间存在相对顺序,最后除
考虑使用容斥来处理每个元素出现次数不超过 的限制,枚举 的整数划分,并钦定集合内部完全相等,集合之间不钦定不相等
设 表示 个元素的集合的贡献系数,那么某种划分的容斥系数就是每个集合贡献系数并将集合无序化
设 表示 个元素的集合的实际贡献系数,根据题意有 ,对于一个划分数会将该块拆成若干个大小任意的块,这符合 的计算定义,那么
对于每个划分数求出来对应的方案数,继续使用平凡容斥去掉不超过 的限制,也就是先做一个简单的记录超过数字数量和集合奇偶性的 算方案数
写出来一个大小为 的集合在任意选择长度时在长度这维的 乘起来得到非最大值长度总和的
那么其指数 的系数的和附加上选择最大值的方案 可以直接贡献给答案,对于 的长度总和,其贡献系数为 的形式,拆开之后可以求导再求系数和得到正确值
系数前缀和可以通过将函数 得到
剩下的问题是求 ,使用 波斯坦-茉莉 算法解决,也就是说:
只有偶数项系数非零,提取出来 的奇数/偶数项讨论 的奇偶性变成子问题即可
计算时注意根据容斥原理指数发生了改变,那么对于 的 ,贡献系数变成了 的形式
Code Display
#define poly vector<int>
const int N=110;
inline poly Mul(const poly &A,const poly &B){
int n=A.size(),m=B.size();
vector<int> c(n+m-1);
for(int i=0;i<n;++i){
for(int j=0;j<m;++j) ckadd(c[i+j],mul(A[i],B[j]));
}
return c;
}
inline poly deriv(const poly &a){
int n=a.size();
if(n==1) return {0};
vector<int> b(n-1);
for(int i=1;i<n;++i) b[i-1]=mul(a[i],i);
return b;
}
int fac[N],ifac[N],inv[N],n,K,Y,X,L;
inline int Recurrence(vector<int> f,vector<int> g,int n){
while(n){
vector<int> tmp=g;
for(int i=1;i<tmp.size();i+=2) tmp[i]=del(0,tmp[i]);
f=Mul(f,tmp); g=Mul(g,tmp);
int sizmx=-1;
for(int i=(n&1);i<f.size();i+=2) f[i/2]=f[i],sizmx=i/2;
f.resize(sizmx+1);
sizmx=-1;
for(int i=0;i<g.size();i+=2) g[i/2]=g[i],sizmx=i/2;
g.resize(sizmx+1);
n>>=1;
}
if(f.size()) return mul(f[0],ksm(g[0],mod-2));
else return 0;
}
int f[N],ton[N],dp[2][N],tmp[2][N];
inline int calc(vector<int> cur){
int coef=1,sum=0;
for(int t:cur) ckmul(coef,mul(f[t],ifac[t])),ton[t]++;
rep(i,1,n-1) ckmul(coef,ifac[ton[i]]),ton[i]=0;
vector<int> deno1={1};
for(int t:cur){
vector<int> tmp={1};
for(int j=1;j<t;++j) tmp.emplace_back(0);
tmp.emplace_back(mod-1);
deno1=Mul(deno1,tmp);
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int t:cur){
rep(p,0,1) rep(j,0,n-1) if(dp[p][j]){
ckadd(tmp[p][j],dp[p][j]);
ckadd(tmp[p^1][j+t],dp[p][j]);
}
rep(p,0,1) rep(j,0,n-1) dp[p][j]=tmp[p][j],tmp[p][j]=0;
}
poly pre=Mul(deno1,{1,mod-1});
poly nume2=Mul(deriv(deno1),{0,1});
poly deno2=Mul(deno1,Mul(deno1,{1,mod-1}));
for(int i=0;i<nume2.size();++i) nume2[i]=del(0,nume2[i]);
for(int i=0;i<n;++i){
if(dp[0][i]==dp[1][i]) continue;
int res=0;
int lef=L-i*(X+1),rig=n*L-i*(X+1);
if(lef>=0){
res-=Recurrence({1},pre,lef)*(L+1-Y)%mod;
}
if(rig>=0){
res+=Recurrence({1},pre,rig)*(L+1-Y)%mod;
}
lef=Y-i*(X+1); rig=L-i*(X+1);
if(rig>=0){
res+=Recurrence(nume2,deno2,rig)-lef*Recurrence({1},pre,rig)%mod;
}
if(lef>=0){
res-=Recurrence(nume2,deno2,lef)-lef*Recurrence({1},pre,lef)%mod;
}
res=(res%mod+mod)%mod;
ckadd(sum,mul(res,del(dp[0][i],dp[1][i])));
}
return mul(coef,sum);
}
signed main(){
freopen("dish.in","r",stdin); freopen("dish.out","w",stdout);
n=100; fac[0]=inv[0]=1;
for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
n=read(); L=read(); Y=read(); X=read(); K=read();
poly vec(K+1),pw={1};
for(int i=1;i<=K;++i) vec[i]=1;
for(int i=1;i<=n-1;++i){
pw=Mul(pw,vec);
if(pw.size()>n) pw.resize(n);
for(int j=1;j<pw.size();++j){
if(i&1) ckadd(f[j],mul(inv[i],pw[j]));
else ckdel(f[j],mul(inv[i],pw[j]));
}
}
rep(i,1,n-1) ckmul(f[i],fac[i]);
int ans=0;
function<void(int,int,vector<int>)>dfs=[&](const int rem,int lst,vector<int> cur){
if(rem==0) return ckadd(ans,calc(cur));
if(rem<lst) return ;
for(int i=lst;i<=rem;++i){
cur.emplace_back(i);
dfs(rem-i,i,cur);
cur.pop_back();
}
return ;
};
dfs(n-1,1,{});
print(ans);
return 0;
}
字符串
判定合法的过程可以通过维护一个栈来实现,如果栈顶和新加入的元素相同就弹栈否则压入,如果最后栈空了就合法
尝试分治:计算交换的两个元素跨过区间中点 的数量,提前维护维护 的压栈情况
判定最终合法的条件是 入栈出栈的结果和 的入栈出栈的结果为回文串
枚举左侧删掉字符的下标 以及替换成了哪个字符,算出来 的两个栈再算出来 的栈和 的栈的 ,剩下的元素哈希起来扔到哈希表里面
右边也枚举哪个元素被替换成了什么,答案的计算就是对于每种情况去哈希表里面查询
Code Display
const int N=1e5+10;
char s[N];
int n,a[N],ans;
ull pw[N],bas=13331;
struct Stack{
int stk[N],top;
ull hsl[N],hsr[N];
inline void ins(int x){
if(top&&x==stk[top]) --top;
else{
stk[++top]=x;
hsl[top]=hsl[top-1]*bas+x;
hsr[top]=hsr[top-1]+pw[top-1]*x;
}
}
inline ull get_hs(int len){
return hsl[top]-hsl[top-len]*pw[len];
}
}lef,rig,tmp;
inline int calc_lcp(Stack &a,Stack &b){
int l=1,r=min(a.top,b.top),res=0;
while(l<=r){
int mid=(l+r)>>1;
if(a.get_hs(mid)==b.get_hs(mid)) l=mid+1,res=mid;
else r=mid-1;
}
return res;
}
inline void solve(int l,int r){
if(l==r) return ;
int mid=(l+r)>>1;
rep(i,l,mid) lef.ins(a[i]);
solve(mid+1,r);
Down(i,mid,l) lef.ins(a[i]);
Down(i,r,mid+1) rig.ins(a[i]);
solve(l,mid);
rep(i,l,mid) lef.ins(a[i]);
tmp.top=0;
unordered_map<ull,int> mp[4][4];
for(int i=mid;i>=l;--i){
lef.ins(a[i]);
for(int t=1;t<=3;++t) if(a[i]!=t){
lef.ins(t);
int lcp=calc_lcp(lef,tmp);
mp[a[i]][t][lef.hsl[lef.top-lcp]*pw[tmp.top-lcp]+tmp.hsr[tmp.top-lcp]]++;
lef.ins(t);
}
tmp.ins(a[i]);
}
tmp.top=0;
for(int i=mid+1;i<=r;++i){
rig.ins(a[i]);
for(int t=1;t<=3;++t) if(a[i]!=t){
rig.ins(t);
int lcp=calc_lcp(rig,tmp);
ull cur=rig.hsl[rig.top-lcp]*pw[tmp.top-lcp]+tmp.hsr[tmp.top-lcp];
if(mp[t][a[i]].count(cur)) ans+=mp[t][a[i]][cur];
rig.ins(t);
}
tmp.ins(a[i]);
}
return ;
}
signed main(){
freopen("string.in","r",stdin); freopen("string.out","w",stdout);
scanf("%s",s+1); n=strlen(s+1);
pw[0]=1;
rep(i,1,n){
pw[i]=pw[i-1]*bas;
a[i]=s[i]-'a'+1;
}
solve(1,n);
print(ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律