模拟赛第三学期
9.20
You have no egg!
9.21
100+100+100+0=300pts。
T1
给两个矩阵
显然只需进行黑白染色即可,注意特判哦:)。
T2
给一个 01 串和一个字符串,第
自己维护一下后缀就行,当然可以用文艺平衡树。
T3
P10647
贪心即可,证明不会。
用大根堆即可优化到
T4
P9000
扫码猎奇线段树。
9.25
0+0+0+0=0pts
评测事故,第一次爆零,纪念一下。
实际 100+20+100+0=220pts,rk1.
T1
纯爆搜,但是不好写,因为我太菜了。
T2
神秘 DP,只会暴力。
T3
P10065
结论题,猜到是平面图即可使用并查集通过。
T4
不会。
10.4
45+100+0+20=165pts,rk6。
怒挂 75pts,被所有人打爆。
T1
有三个罗盘,周期分别为
若无法实现,报告无解。
列几个方程,然后爆搜,注意限制条件。
可以循环枚举的千万不要乱 bfs!千万不要乱特判!
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define PID pair<int, double>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}using namespace IO;
int p1,r1,p2,r2,p3,r3;
int ans=inf;
inline int A(int x1, int x2, int x3){
return p1*x1-p2*x2+p3*x3-(r1+r3-r2);
}
inline int B(int x1, int x2, int x3){
return p1*x1+p2*x2-p3*x3-(r1-r3+r2);
}
inline int C(int x1, int x2, int x3){
return -p1*x1+p2*x2+p3*x3-(r3+r2-r1);
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
p1=read(),r1=read(),p2=read(),r2=read(),p3=read(),r3=read();
for(rg int i=0;i<=500;i++){
for(rg int j=0;j<=500;j++){
for(rg int k=0;k<=500;k++){
int a=A(i,j,k),b=B(i,j,k),c=C(i,j,k);
if(a>=0&&b>=0&&c>=0&&a%2==0&&b%2==0&&c%2==0){
ans=min(ans,(a+b+c)/2);
}
}
}
}
if(ans==inf) puts("Help Me, Mr. ShenJun!");
else write(ans);
return 0;
}
T2
求
对
赛时猜出答案于是通过。
具体推导:
首先有(上指标翻转):
于是:
根据范德蒙德卷积:
翻转回来:
T3
暴力 30pts 不写是人?
原题 P4757。
考虑 DP,设
但是并不好转移,因为还要考虑路径
然后再考虑路径内的点的贡献,可以考虑把
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int N=1e3+10;
const int M=(1<<10)+10;
int n,m,dp[N],g[M],id[N];
vector<int> e[N];
vector<PII> q[N];
int tim,f[N],dep[N],son[N],size[N],dfn[N],top[N];
namespace BIT{
int tree[N];
inline int lbt(int x){return x&-x;}
inline void Clear(){for(rg int i=0;i<=n;i++) tree[i]=0;}
inline void add(int x, int d){while(x<=n) tree[x]+=d,x+=lbt(x);}
inline int query(int x){int res=0;while(x>0){res+=tree[x],x-=lbt(x);}return res;}
}using namespace BIT;
inline void init(){
tim=0;
for(rg int i=0;i<=n;i++){
e[i].clear(),q[i].clear();
dp[i]=f[i]=son[i]=dep[i]=0;
}
Clear();
}
inline void dfs1(int u, int fa){
dep[u]=dep[fa]+1,size[u]=1,f[u]=fa;
for(int v:e[u]){
if(v==fa) continue;
dfs1(v,u);size[u]+=size[v];
if(!son[u]||size[son[u]]<size[v]) son[u]=v;
}
}
inline void dfs2(int u, int tp){
dfn[u]=++tim,top[u]=tp;
if(!son[u]) return;
dfs2(son[u],tp);
for(int v:e[u]) if(v!=f[u]&&v!=son[u]) dfs2(v,v);
}
inline int LCA(int x, int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=f[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline void DP(int u, int fa){
int tot=0;
for(int v:e[u]) if(v^fa) DP(v,u);
for(int v:e[u]) if(v^fa) id[v]=tot++;
for(rg int i=0;i<(1<<tot);i++) g[i]=0;
for(int v:e[u]) if(v^fa) g[1<<id[v]]=dp[v];
for(auto p:q[u]){
int qu=p.fi,qv=p.se;
int zu=0,zv=0;
if(qu^u){
for(int v:e[u]){
if(v==fa) continue;
if(dfn[v]<=dfn[qu]&&dfn[qu]<=dfn[v]+size[v]-1) zu=(1<<id[v]);
if(zu) break;
}
}
if(qv^u){
for(int v:e[u]){
if(v==fa) continue;
if(dfn[v]<=dfn[qv]&&dfn[qv]<=dfn[v]+size[v]-1) zv=(1<<id[v]);
if(zv) break;
}
}
g[zu|zv]=max(g[zu|zv],dp[qu]+dp[qv]+query(dfn[qu])+query(dfn[qv])+1);
}
for(rg int i=0;i<(1<<tot);i++)
for(rg int j=i;j;j=(j-1)&i) g[i]=max(g[i],g[j]+g[i^j]);
dp[u]=g[(1<<tot)-1];
for(int v:e[u]){
if(v==fa) continue;
int sum=g[((1<<tot)-1)^(1<<id[v])];
add(dfn[v],sum),add(dfn[v]+size[v],-sum);
}
}
inline void Ruby(){
n=read();init();
for(rg int i=1;i<n;i++){
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,0);dfs2(1,1);
m=read();
for(rg int i=1;i<=m;i++){
int u=read(),v=read();
q[LCA(u,v)].emplace_back(u,v);
}
DP(1,0);
write(dp[1]),puts("");
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
int T=read();
while(T--) Ruby();
return 0;
}
T4
20pts,写太史了,本来 40pts。
CF1601F。
10.5
100+100+20+0=220pts,rk3,罚时惨败 ycy 和 hlc。
rk3 为何 rating-503?看来他们 rating 都是从我这扣的。
T1
爆搜,难度大概 PJT3 左右。
T2
原题
是这个的简单版,只用算一个
考虑每个点对答案的贡献,钦定
这个东西
再看原题,现在考虑去统计每个点为根的情况,那么
于是
拆贡献,设
令
后面变成了加法卷积的形式,NTT 即可,时间复杂度
注意
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int mod=924844033;
const int G=5,Gi=554906420;
const int N=2e5+10,M=6e5+10;
int n,a[M],b[M],sz[N];
int R[M],Lim=1,len;
int fac[N],inv[N];
vector<int> e[N];
inline int qpow(int x, int y){
int res=1;
while(y>0){
if(y&1) res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res;
}
inline void dfs(int u, int fa){
sz[u]=1;
for(int v:e[u]) if(v^fa) dfs(v,u),sz[u]+=sz[v];
if(fa) a[sz[u]]++,a[n-sz[u]]++;
}
inline void NTT(int *a, int flag){
for(rg int i=0;i<Lim;i++) if(i<R[i]) swap(a[i],a[R[i]]);
for(rg int h=1;h<Lim;h<<=1){
int W=qpow((flag==1?G:Gi),(mod-1)/(h<<1))%mod;
for(rg int j=0;j<Lim;j+=(h<<1)){
int w=1;
for(rg int k=j;k<j+h;k++){
int x=a[k]%mod,y=w*a[k+h]%mod;
a[k]=(x+y)%mod,a[k+h]=(x-y+mod)%mod;
w=w*W%mod;
}
}
}
if(flag==-1){
int in=qpow(Lim,mod-2)%mod;
for(rg int i=0;i<Lim;i++) a[i]=a[i]%mod*in%mod;
}
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<n;i++){
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
fac[0]=1;
for(rg int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2)%mod;
for(rg int i=n;i>=1;i--) inv[i-1]=inv[i]*i%mod;
for(rg int i=1;i<=n;i++) a[i]=a[i]*fac[i]%mod;
for(rg int i=0;i<=n;i++) b[n-i]=inv[i]%mod;
while(Lim<=2*n) Lim<<=1,len++;
for(rg int i=0;i<Lim;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(len-1));
NTT(a,1),NTT(b,1);
for(rg int i=0;i<Lim;i++) a[i]=a[i]*b[i]%mod;
NTT(a,-1);
for(rg int i=1;i<=n;i++){
int res1=fac[n]%mod*inv[i]%mod*inv[n-i]%mod*n%mod;
int res2=a[n+i]%mod*inv[i]%mod;
write((res1-res2+mod)%mod),puts("");
}
return 0;
}
T3
求有多少个长度为
1.
2.
考虑指数型生成函数:
于是
T4
原题
10.6
100+100+70+0=270pts,rk4
T1
你遇见了一只玩偶,它的肚子上有一个计数器。
这个计数器的计数上限是
你需要执行恰好
值得注意的是,在每次操作之后,你都可以看到计数器当前的显示值。你的得分被定义为计数器最终的显示值,且你的目标是最大化这一得分。
求在最优策略下的期望得分。
一个期望 dp,设
然后考虑转移,枚举
有:
时间复杂度
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int N=1e5+10;
const int M=1e2+10;
int n,m,l1,l2,r1,r2;
db dp[N][M];
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read(),m=read(),l1=read(),r1=read(),l2=read(),r2=read();
for(int i=0;i<m;i++) dp[0][i]=i;
for(int i=1;i<=n;i++){
db k1=0,k2=0;
for(int k=l1;k<=r1;k++) k1+=dp[i-1][k%m];
for(int k=l2;k<=r2;k++) k2+=dp[i-1][k%m];
dp[i][0]=max(k1*1.0/(r1-l1+1),k2*1.0/(r2-l2+1));
for(int j=1;j<m;j++){
k1-=dp[i-1][(l1+j-1)%m];
k1+=dp[i-1][(r1+j)%m];
k2-=dp[i-1][(l2+j-1)%m];
k2+=dp[i-1][(r2+j)%m];
dp[i][j]=max(k1*1.0/(r1-l1+1),k2*1.0/(r2-l2+1));
}
}
printf("%.2lf",dp[n][0]);
return 0;
}
T2
9.21 T3 原题。
T3
CF1305F
首先答案一定小于等于奇数个数,否则可以把奇数变成偶数。然后一定有一半及以上的元素加一、减一或不变,于是答案一定是这些元素或操作后元素的因数。
而每个数被加一、减一、不变的概率都大于等于二分之一,于是我们随机选 50 到 60 个数把它们及操作后数质因数分解,这样错误的概率是
T4
原题
10.12
100+30+0+0=130pts
自我反思吧,为什么这么菜。
T1 乱写了个假然后过了,拼劲全力写的 T2T3 挂成一个钢,然后心态爆炸 T4 也没看,痛失 30pts。
T1
原题
T1 放紫是啥?
钦定排序后前一半都组合起来,后一半全单选,枚举断点然后直接做,这样复杂度是
考虑避免这种情况,可以发现取单个数相当于取这个数和
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int N=2e4+10;
int a[N],b[N],n;
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<=n;i++) b[i]=a[i]=read();
int ans=INF;
for(rg int len=n;len<=n*2;len++){
for(rg int i=n+1;i<=len;i++) a[i]=0;
sort(a+1,a+1+len);
int mx=-INF,mi=INF;
for(rg int i=1;i<=(len>>1);i++){
mx=max(mx,a[i]+a[len-i+1]);
mi=min(mi,a[i]+a[len-i+1]);
}
if(len&1){
mx=max(mx,a[len/2+1]);
mi=min(mi,a[len/2+1]);
}
ans=min(ans,mx-mi);
for(rg int i=1;i<=n;i++) a[i]=b[i];
}
write(ans);
return 0;
}
插排
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int N=4e4+10;
int a[N],b[N],n;
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<=n;i++) b[i]=a[i]=read();
sort(a+1,a+1+n);
int ans=INF,T=n+1;
while(T--){
int mi=inf,mx=-inf;
for(rg int i=1;i<=(n>>1);i++){
mi=min(mi,a[i]+a[n-i+1]);
mx=max(mx,a[i]+a[n-i+1]);
}
if(n&1){
mi=min(mi,a[n/2+1]);
mx=max(mx,a[n/2+1]);
}
ans=min(ans,mx-mi);n++;
for(rg int i=n;i>1&&a[i-1]>a[i];i--) swap(a[i-1],a[i]);
}
write(ans);
return 0;
}
T2
给定一个字符串
-
- 对于
\forall i \in [2,n],T_i = T_{i−1} +S_i +T_{i−1} 。 其中+ 表示拼接。 求T_n 所有子序列中与P 相同的个数,答案对998244353 取模。
未知原因挂 30pts。
60pts 做法,直接把最终串生成出来 dp,复杂度是
考虑生成操作,每次生成是
数组第
字典树写炸并非人类。
密码的 60 个 bit 1ll<<bit 不加 ll 是人类吗?警钟撅烂。
首先
可惜了,差点写出正解。
首先我们考虑对于一个询问区间
当前
考虑把询问差分成
不过容易发现差分一下常数乘个 2,容易被卡,于是我们可以把这个过程当成线段树去做,区间取
写法上要注意很多细节问题,不然炸成钢,本人菜到调了 2d 最后在 Tyih 巨佬的帮助下调出来了,\bx\bx。
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int mod=998244353;
const int N=1e5+10;
int n,q,a[N],bit[65],p[65];
vector<int> f[N<<6];
namespace Trie{
int ch[N<<6][2],cnt=1,sz[N<<6],d[N<<6];
inline void insert(int x){
int u=1;sz[u]++;
for(rg int i=59;i>=0;i--){
int bit=(x>>i)&1;
if(!ch[u][bit]) ch[u][bit]=++cnt;
sz[u=ch[u][bit]]++;
}
}
inline void build(int u, int dep){
f[u].resize(sz[u]+1);d[u]=dep;
int ls=ch[u][0],rs=ch[u][1];
if(!ls&&!rs){
for(rg int i=1;i<=sz[u];i++) f[u][i]=0;
return;
}
if(ls) build(ls,dep-1);
if(rs) build(rs,dep-1);
for(rg int i=1;i<=sz[u];i++){
int x=0,y=0;
if(i<=sz[ls]) x=f[ls][i]%mod;
else x=(f[rs][i-sz[ls]]+bit[dep-1]*bit[dep-1]%mod)%mod;
if(i<=sz[rs]) y=f[rs][i]%mod;
else y=(f[ls][i-sz[rs]]+bit[dep-1]*bit[dep-1]%mod)%mod;
f[u][i]=(x+y)%mod;
}
}
inline int solve(int u, int l, int r, int ql, int qr, int k){
if(l>=ql&&r<=qr) return f[u][k];
int mid=l+r>>1,ls=ch[u][0],rs=ch[u][1],ans=0;
if(ql<=mid){
if(sz[ls]>=k) ans=(ans+solve(ls,l,mid,ql,qr,k))%mod;
else ans=(ans+solve(rs,l,mid,ql,qr,k-sz[ls])+1ll*bit[d[u]-1]*((min(qr,mid)-max(l,ql)+1)%mod))%mod;
}
if(qr>mid){
if(sz[rs]>=k) ans=(ans+solve(rs,mid+1,r,ql,qr,k))%mod;
else ans=(ans+solve(ls,mid+1,r,ql,qr,k-sz[rs])+1ll*bit[d[u]-1]*((min(qr,r)-max(mid+1,ql)+1)%mod))%mod;
}
return ans%mod;
}
}using namespace Trie;
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read(),q=read();
for(rg int i=1;i<=n;i++) insert(read());
bit[0]=p[0]=1;
for(rg int i=1;i<=61;i++) bit[i]=(bit[i-1]<<1)%mod,p[i]=p[i-1]<<1;
build(1,60);
while(q--){
int l=read(),r=read(),k=read();
write(solve(1,0,(1ll<<60)-1,l,r,k)%mod);
puts("");
}
return 0;
}
10.19
100+100+0+20=220pts,rk10。
T3 int pushup(int i) 100pts->0pts。
警钟撅烂,写代码一定要开 -Wall。
T1
https://www.luogu.com.cn/article/1ns2c1tf
糖丸了。
T2
原题
考虑设状态
考虑转移,非常朴素:
考虑 当然想用线段树多个 log 也行。
考虑
时间复杂度
T3
原题
考虑设
这是因为所有与
于是我们可以枚举
T4
给定两棵节点数为
暴力有 40pts,可惜因为没写 RMQLCA 挂了 20pts,wssb。
10.22
100+100+40+0=240pts
T1
给定两张
每次可以交换两个点的编号,这样它们对应的边也会随之改变,求至少需要多少次操作才能将
定义两张图相同,当且仅当对于每条边在两张图中的存在性相同。
交换显然可以看成轮换,可以考虑爆搜,枚举全排列即可。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=11,INF=1e9;
int n,p[N],vis[N];
char a[N][N],b[N][N];
bool chk(){
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
if(a[p[i]][p[j]]!=b[i][j])return 0;
}
return 1;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
for(int i=1;i<=n;i++)scanf("%s",b[i]+1);
iota(p,p+1+n,0);
int ans=INF;
do{
if(!chk())continue;
int cnt=n;
fill(vis,vis+1+n,0);
for(int i=1;i<=n;i++)if(!vis[i]){
for(int x=i;!vis[x];x=p[x])vis[x]=1;
cnt--;
}
ans=min(ans,cnt);
}while(next_permutation(p+1,p+1+n));
cout<<ans;
return 0;
}
T2
给定一个长度为
一个很典的 dp。
显然可以先预处理出每个前缀的
设
然后前缀和优化一下,转移的复杂度是
const int mod=998244353;
const int N=2e3+10;
int n,a[N],p[N][N],f[N][N],g[N][N];
inline int qpow(int x, int y, int p){
int ans=1;
while(y>0){
if(y&1) ans=ans*x%p;
x=x*x%p,y>>=1;
}
return ans%p;
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<=n;i++){
a[i]=read();
for(rg int j=1;j<=i;j++)
p[i][j]=(p[i-1][j]+qpow(a[i]%j,j,j))%j;
}
g[0][0]=1;
for(rg int j=1;j<=n;j++){
for(rg int i=1;i<=n;i++){
(f[i][j]+=g[p[i][j]%j][j-1])%=mod;
(g[p[i][j]%j][j-1]+=f[i][j-1])%=mod;
}
}
int ans=0;
for(rg int k=1;k<=n;k++) ans=(ans+f[n][k])%mod;
write(ans);
return 0;
}
T3
给定一张
你只需求出答案在模
很明显这是个状压 dp,考虑让这玩意变成 0-index。
考虑容斥,转换为不存在
于是答案会变成:
其中
条信息,每个信息可以表示为
但是不幸的是,信息在到你手上之前先经过了跳蚤手上,跳蚤为了让你不能定量计算,于是加入了一条伪造的信息。
你无法分辨哪一条是伪造的信息,因此你决定对于每条信息,如果除了这条信息以外的信息都是真的话,你的难受程度总和是多少。
有一个很典的做法,对于每只虱子分别拆贡献,做一个扫描线,记录每个虱子出现的区间然后离散化一下,区间加
T2
非常有意思的题,不过赛时红温效应导致做不出来,揭示了我是废物的本质。
有一个
首先我们考虑取两个点,这两点连线的线段上没有其他点了,把它绕中点顺时针旋转一个微小的角度,一定可以得到一种分割方案。所以这种线段的数量是答案的一个下界。对于每一种分割的方案,一定存在至少一条线段使其可以通过顺时针旋转得到,所有这又是答案的一个上界。
于是答案就是任意两个可见点的连线段数量,可以
O(n^2)
枚举可见线段向量的
n=read(),p=read();
ll ans=0;
for(rg int i=0;i<=n;i++){
for(rg int j=0;j<i;j++){
if(gcd(i,j)==1) ans=(ans+1ll*(n-i)*(n-j)%p)%p;
}
}
ans=(ans*4ll%p-2ll*(n-1)%p+p)%p;
write(ans%p);
return 0;
O(n)
据说答案为:
1000 年内无人能推出答案。
T3
原神
T4
忘了。
11.20
100+100+100+20+320pts。
T1
有一个值域为
首先值域为
n=read(),m=read();
for(rg int i=1;i<=n;i++) v[a[i]=read()]++;
int ans=0;
for(rg int i=1;i<=m;i++){
for(rg int k=i;k<=m;k+=i){
int mi=min(i,k/i),mx=max(i,k/i);
if((mx-mi)%2==0) ans+=v[k]*v[(mx-mi)/2];
}
}
write(ans/2);
return 0;
T2
原题
首先我觉得这题不应该评紫,数据范围只有这么小一点,
除了朴素的线段树以外本人还有新做法,考虑分治,这样我们只需求
然后我们可以分四类讨论,假设
如果
的
考虑固定
inline void solve(int l, int r){
if(l==r) return;
int mid=l+r>>1,ql,qr;
solve(l,mid);solve(mid+1,r);
mx[mid]=mi[mid]=a[mid],mx[mid+1]=mi[mid+1]=a[mid+1];
for(rg int i=mid-1;i>=l;i--) mx[i]=max(mx[i+1],a[i]),mi[i]=min(mi[i+1],a[i]);
for(rg int i=mid+2;i<=r;i++) mx[i]=max(mx[i-1],a[i]),mi[i]=min(mi[i-1],a[i]);
for(rg int L=mid;L>=l;L--){
int R=L+mx[L]-mi[L];
if(R<=r&&R>mid && mx[L]>mx[R] && mi[L]<mi[R]) ans++;
}
for(rg int R=mid+1;R<=r;R++){
int L=R+mi[R]-mx[R];
if(L>=l&&L<=mid && mx[R]>mx[L] && mi[R]<mi[L]) ans++;
}
ql=qr=mid+1;
for(rg int L=mid;L>=l;L--){
while(mx[qr]<mx[L]&&qr<=r) p[mi[qr]+qr+N]++,qr++;
while(mi[ql]>mi[L]&&ql<qr) p[mi[ql]+ql+N]--,ql++;
ans+=p[mx[L]+L+N];
}while(ql<qr) p[mi[ql]+ql+N]--,ql++;
ql=qr=mid;
for(rg int R=mid+1;R<=r;R++){
while(mx[ql]<mx[R]&&ql>=l) p[mi[ql]-ql+N]++,ql--;
while(mi[qr]>mi[R]&&qr>ql) p[mi[qr]-qr+N]--,qr--;
ans+=p[mx[R]-R+N];
}while(ql<qr) p[mi[qr]-qr+N]--,qr--;
}
T3
原题
首先看到求最大值的最小值,考虑二分,那么问题就变成了对于一个
考虑 dp,设
注意到对于
const int N=4010;
int c[N][N],n;
bitset<N> f[N];
inline bool check(int mid){
for(rg int i=1;i<=n;i++) f[i].reset(),f[i][i-1]=1;
for(rg int l=n-1;l>=1;l--){
for(rg int r=l+1;r<=n;r+=2){
if(f[l+1][r-1]&&!f[l][r]&&c[l][r]<=mid){
f[l][r]=1,f[l]|=f[r+1];
}
}
}
return f[1][n];
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<=n;i++){
for(rg int j=i+1;j<=n;j+=2)
c[i][j]=read();
}
int l=1,r=n*n/4,ans=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
write(ans);
return 0;
}
T4
原题
打了个暴力跑路,突然发现还有个 bitset 优化做法,复杂度除了个
11.21
草泥马的忘保存了,上次写的被删完了。
T1
原题
差分一下,钦定前一半为正,后一半为负,枚举极值点,在前半段取值范围
T2
原题
范围小一点可以区间 dp。
考虑设
长得有点像倍增,复杂度
T3
原题
考虑把黑子树全删掉。
考虑一条回路,答案为
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=INT_MAX;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int N=1e5+10;
char s[N];
int n,deg[N],val[N],u[N],v[N];
int f[N],root,num,len,tot;
bool vis[N];
vector<int> g[N],e[N];
inline void dfs(int u, int fa){
f[u]=val[u];
for(int v:e[u]){
if(v==fa) continue;
dfs(v,u);
len=max(len,f[u]+f[v]);
f[u]=max(f[u],f[v]+val[u]);
}
}
inline void rebuild(){
queue<int> q;
for(rg int i=1;i<=n;i++) if(deg[i]==1&&s[i]=='B') q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=1;
for(int v:g[u]){
if(!vis[v]){
deg[v]--;
if(deg[v]==1&&s[v]=='B') q.push(v);
}
}
}
memset(deg,0,sizeof deg);
for(rg int i=1;i<n;i++){
if(!vis[u[i]]&&!vis[v[i]]){
e[u[i]].push_back(v[i]);
e[v[i]].push_back(u[i]);
deg[u[i]]++,deg[v[i]]++;
}
}
for(rg int i=1;i<=n;i++){
if(vis[i]) continue;
if((s[i]=='W'&°[i]%2==0)||(s[i]=='B'&°[i]%2==1)) val[i]=2,num++;
}
for(rg int i=1;i<=n;i++) if(!vis[i]) root=i,tot++;
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<n;i++){
u[i]=read(),v[i]=read();
g[u[i]].push_back(v[i]),g[v[i]].push_back(u[i]);
deg[u[i]]++,deg[v[i]]++;
}
scanf("%s",s+1);
rebuild();
dfs(root,0);
if(tot==0) return write(0),0;
// write(tot),pc(' '),write(num),pc(' '),write(len),puts("");
write((tot-1)*2+num-len);
return 0;
}
T4
原题
50pts 做法:
枚举根节点,处理出
正解点分治本人不会。
11.24
100+100+100+0=300pts
T1
原题
赛时题目更简单,要求是排列。
考虑找其中最多有几个点不用操作,发现这些点一定是一条值域连续的子序列,直接 dp 即可,核心代码两行:
f[a[i]=f[a[i]-1]+1;
ans=max(ans,f[a[i]]);
T2
原题
依旧考虑最多有几个点不用操作,设
第二个转移需要注意不能加上
for(rg int i=1;i<=n;i++){
a[i]=read();
if(!l[a[i]]) l[a[i]]=i;
r[a[i]]=i;
}
for(rg int i=n;i>=1;i--){
f[i]=f[i+1],cnt[a[i]]++;
if(i!=l[a[i]]) f[i]=max(f[i],cnt[a[i]]);
else f[i]=max(f[i],cnt[a[i]]+f[r[a[i]]+1]);
}
write(n-f[1]);
T3
智斗程度不亚于钟离假死。
原题
一百年内无人能看懂这题。
首先神秘结论:猜出来的人一定是最大数。
然后讲一下原理,当 A 猜的时候如果另外两个人数字相等那么 A 一定是他们的和,因为值域没有
所以可以考虑写一个递归,类似更相减损,减到两个数相等返回即可,这个状态的步数一定小于等于三。
复杂度
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=2e18;
const int inf=INT_MAX;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int N=505,M=3e4+10;
int t,n,cnt,pre[M],nxt[M];
inline int dfs(int x, int y, int p){
if(x==y) return p+1;
return (x>y)?(dfs(y,x-y,(p+2)%3)+1):(dfs(y-x,x,(p+1)%3)+2);
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
t=read(),n=read();
int fi=(t-1)%3;
for(rg int i=1;i<n;i++) if(dfs(i,n-i,fi)==t) pre[++cnt]=i,nxt[cnt]=n-i;
write(cnt),puts("");
if(fi==0){
for(rg int i=cnt;i>=1;i--) write(n),pc(' '),write(nxt[i]),pc(' '),write(pre[i]),puts("");
}else if(fi==1){
for(rg int i=1;i<=cnt;i++) write(pre[i]),pc(' '),write(n),pc(' '),write(nxt[i]),puts("");
}else{
for(rg int i=cnt;i>=1;i--) write(nxt[i]),pc(' '),write(pre[i]),pc(' '),write(n),puts("");
}
return 0;
}
T4
原题
不会,后面忘了。
11.25
100+0+100+100=300pts,T2 保灵是因为难度不升序排。
可以看到 Ruby 比我强。
T1
原题
考虑一个素因子集合
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=2e18;
const int inf=INT_MAX;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
int fac[5]={2,3,5,7,11};
int cnt[5];
set<int> S,T;
signed main(){
freopen("P.in","r",stdin);
freopen("P.out","w",stdout);
int k=read();
for(rg int i=1;i<=2*k*k;i++){
int num=i;
for(rg int j=0;j<5;j++) while(num%fac[j]==0) num/=fac[j];
if(num==1){
S.emplace(i);
for(rg int j=0;j<5;j++) if(i%fac[j]==0) cnt[j]++;
if(S.size()>=k) break;
}
}
for(rg int i=4;i>=0;i--){
T.clear();
for(int j:S) if(j%fac[i]) T.emplace(j);
while(cnt[i]*2<k){
auto it=T.begin();
int num=*it;
T.erase(it);
if(S.find(num*fac[i])==S.end() && num*fac[i]<=2*k*k){
S.erase(S.find(num));
S.emplace(num*fac[i]);
cnt[i]++;
}
}
}
for(int i:S) write(i),pc(' ');
return 0;
}
T2
原题
考虑按最大值分治,然后考虑两端的答案,显然由于数据不均匀,要用小的一端算大的一端防止复杂度退化。类似启发式合并,这个复杂度是
然后设当前最大值为
总复杂度
T3
原题
首先对于每个点都有一堆区间的限制,要求这个点必须在这些区间内且在每个限制中都是 min。
对于第一个条件,显然这个点一定在所有限制的交集,这个是好做的,可以把点的存在范围缩小到一个区间,如果没有限制则默认为
对于第二个条件,这个点在所有一定是最小值,考虑统计每个位置有多少个区间覆盖,然后每个数从小到大贪心地放位置,这样限制是越来越松的,感性理解一下从大到小做肯定不优,因为你还要考虑更小的数不能在当前数的范围内,这样范围越来越小,不优。
具体的,枚举到一个数,给其所有区间减一,表示这个数在这些区间里放置了,把这些限制删掉;然后考虑其存在范围中有没有
n=read(),q=read();
for(rg int i=1;i<=q;i++){
int l=read()+1,r=read()+1,a=read()+1;
v[a].emplace_back(l,r);
if(!L[a]) L[a]=l,R[a]=r;
else L[a]=max(L[a],l),R[a]=min(R[a],r);
}
build(1,1,n);
for(rg int i=1;i<=n;i++){
if(L[i]>R[i]){
for(rg int i=1;i<=n;i++) write(-1),pc(' ');
return 0;
}
if(!L[i]) L[i]=1,R[i]=n;
for(auto p:v[i]) add(1,p.fi,p.se,1);
}
for(rg int i=1;i<=n;i++){
for(auto p:v[i]) add(1,p.fi,p.se,-1);
PII res=query(1,L[i],R[i]);
if(res.fi){
for(rg int i=1;i<=n;i++) write(-1),pc(' ');
return 0;
}
ans[res.se]=i;
add(1,res.se,res.se,inf);
}
for(rg int i=1;i<=n;i++) write(ans[i]-1),pc(' ');
return 0;
T4
原题
不难发现题目的操作就是把
然后考虑固定操作顺序,把操作的增减画成一个折线图,纵坐标是
而我们要满足的条件是
inline int solve(int x){
memset(f,0,sizeof f);
f[0][0]=1;
int C=B[n]-A[n];
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<=i;j++){
int k=A[i]-B[j];
if(C+k>x) continue;
if(i>0) f[i][j]=(f[i][j]+f[i-1][j])%mod;
if(j>0) f[i][j]=(f[i][j]+f[i][j-1])%mod;
}
}
return f[n][n]%mod;
}
signed main(){
freopen("maxz.in","r",stdin);
freopen("maxz.out","w",stdout);
n=read(),L=read(),R=read();
for(rg int i=1;i<=n;i++) a[i]=read(),A[i]=A[i-1]+a[i];
for(rg int i=1;i<=n;i++) b[i]=read(),B[i]=B[i-1]+b[i];
write((solve(R)-solve(L-1)+mod)%mod);
return 0;
}
11.26
100+100+100+0=300pts
T1
傻逼诈骗题骗你爸 40min。
T2
众所周知,合法车牌号必须由阿拉伯数字和大写英文字母构成,每一位共有
显然是个很典的数位 dp,好像也可以直接拆贡献算。
首先满
for(rg int j=0;j<34;j++) cnt[x][j]=(cnt[x][j]+f[i-1]*p[x][i]%mod)%mod;//后 i-1 位的贡献
for(rg int j=0;j<p[x][i];j++) cnt[x][j]=(cnt[x][j]+g[i-1])%mod;//第 i 位的贡献
贴上界,这个上界出现的次数为数的后
复杂度
#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int>
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=2e18;
const int inf=INT_MAX;
namespace IO{
inline int read(){
int x=0,f=1;
char ch=gc();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=gc();
}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
return x*f;
}
inline void write(int x){
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
}
using namespace IO;
const int mod=1e9+7;
const int N=1e6+10;
string str="0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
//h->i n->o
int n,hsh[105],p[2][N];
int cnt[2][50],f[N],g[N];
char s[N];
inline void solve(int x){
int tmp=0;
for(rg int i=n;i>=1;i--) tmp=(tmp*34%mod+p[x][i])%mod;
// puts("114514");
for(rg int i=n;i>=1;i--){
for(rg int j=0;j<34;j++) cnt[x][j]=(cnt[x][j]+f[i-1]*p[x][i]%mod)%mod;
for(rg int j=0;j<p[x][i];j++) cnt[x][j]=(cnt[x][j]+g[i-1])%mod;
tmp=(tmp%mod-g[i-1]%mod*p[x][i]%mod+mod)%mod;
cnt[x][p[x][i]]=(cnt[x][p[x][i]]+tmp+1)%mod;
}
}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
for(rg int i=0;i<34;i++) hsh[(int)str[i]]=i;
n=read();
scanf("%s",s+1);for(rg int i=1;i<=n;i++) p[0][i]=hsh[s[i]];
scanf("%s",s+1);for(rg int i=1;i<=n;i++) p[1][i]=hsh[s[i]];
reverse(p[0]+1,p[0]+1+n);reverse(p[1]+1,p[1]+1+n);
g[0]=1;
for(rg int i=1;i<=n;i++){
f[i]=(f[i-1]*34%mod+g[i-1])%mod;
g[i]=g[i-1]*34%mod;
}
solve(0);solve(1);
for(rg int i=0;i<34;i++){
int res=count(p[0]+1,p[0]+1+n,i);
write(((cnt[1][i]%mod-cnt[0][i]%mod+mod)%mod+res)%mod),pc(' ');
if(str[i]=='H'||str[i]=='N') write(0),pc(' ');
}
return 0;
}
T3
原题
评黑纯恶评。
首先不难发现奇数位一定要是奇数,偶数位一定要是偶数。然后发现一次合法的操作会使全局逆序对数
充分性的话,发现只交换三元组的
于是树状数组做一下就好了,复杂度
T4
不会。
11.27
最后一次模拟赛。
在最后的这一次,我不知道有什么想说的。总的算下来我也打过这么多模拟赛了。从今年寒假开始写模拟赛记录到现在,写了有 6 篇,这些文章见证了我从啥都不会到现在能打两三百分的历程。
可能是因为本人素质比较低,写了不少脏话。
学了这么久的 OI,如今可能真的迎来终点了?我很后悔,我为什么要学竞赛?为了它我失去了尊严,失去了优异文化课成绩,失去了一个好的身体,失去了一段美好的感情,失去了纯洁,失去了天真,失去了善良,失去了半个青春……
可能小学三年级我就不应该答应我妈学编程,可能我应该像 HOT 的那些唐氏小朋友一样天天打游戏然后退学,可能我应该再考烂点让我妈看不见我进复赛的希望,可能我应该再多抄点题解让洛谷把我封号使我破防,可能我不应该认识过氧化氢,可能我应该在初中放弃它去和 wmy 好好交往,可能我应该像那些社会人一样不务正业,可能我不应该去学什么竞赛也不去考什么 qzez,可能我应该选其他竞赛然后发现自己是个废物然后提前退役学习文化课,可能……
但是这些都不可能了,我始终相信这些选择一定不比现实更优。
NOIP2025 RP++
100+60+100+20=280pts。
T1
平面直角坐标系上有
对于每个
然后,你可以从任意点出发,在任意点结束,要求依次标记
每次移动只能让某一维坐标
不难发现
inline int dist(PII a, PII b){return abs(a.fi-b.fi)+abs(a.se-b.se);}
signed main(){
// freopen("114514.in","r",stdin);
// freopen("114514.out","w",stdout);
n=read();
for(rg int i=1;i<=2*n;i++) p[i].fi=read(),p[i].se=read();
PII A=p[2*n],B=p[2*n-1];
f[n][0]=f[n][1]=dist(A,B);
for(rg int i=n-1;i>=1;i--){
PII P=p[2*i],Q=p[2*i-1];
f[i][0]=min(f[i+1][0],f[i+1][1])+dist(A,P)+dist(B,Q);
f[i][1]=min(f[i+1][0],f[i+1][1])+dist(A,Q)+dist(P,B);
A=P,B=Q;
}
write(min(f[1][0],f[1][1]));
return 0;
}
T2
忘取模挂了 40pts。
IMO 2011 D1T1。
规定一个四元组
定义一个四元组的神奇度,为从中选出两个不同元素之和为
求从给定区间
证明
先证明这个神奇度最大为
然后由于
令
由于
所以
分讨: