NOIP 模拟赛 #20
已经好久没写模拟赛题解了啊。。。
A. 邻间的骰子之舞
一个结论,可以打表,每一次复制后跟的粘贴数量要尽量相同,差不超过1,所以枚举复制了几次,然后二分最大的出来答案小于 \(n\) 的数 \(mid\),然后枚举多少个复制后的粘贴数为 \(mid+1\),出来的答案可以 \(O(1)\) 算,大于 \(n\) 直接输出就好了。
点击查看代码
#include<bits/stdc++.h>
#define int __int128
const int maxn=255;
using namespace std;
int n,x,y,tot,ans;
bool check(int x,int f)
{
int temp=1;
for(int i=1;i<=f;i++)
{
temp*=x;
if(temp>n) return 1;
}
return 0;
}
int solve(int f)
{
int l=0,r=n,ans=0;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid+1,f)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
template<typename T> inline T read() {
T X = 0; bool flag = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') flag = 0; ch = getchar();}
while (ch >= '0' && ch <= '9') {X = (X << 1) + (X << 3) + ch - '0'; ch = getchar();}
if (flag) return X;
return ~ (X - 1);
}
template<typename T> inline void write(T X) {
if (X < 0) {putchar('-'); X = ~ (X - 1);}
int s[50], top = 0;
while (X) {s[++top] = X % 10; X /= 10;}
if (!top) s[++top] = 0;
while (top) putchar(s[top--] + '0');
puts("");
return;
}
signed main()
{
freopen("dice.in","r",stdin);
freopen("dice.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
n=read<int>(),x=read<int>(),y=read<int>();
tot=64;
ans=x+y*n;
for(int i=2,temp=0;i<=tot;i++)
{
int m=solve(i),flag=0;
for(int j=0;j<=i;j++)
{
int temp=1;
for(int k=1;k<=j;k++)
{
temp*=(m+1);
if(temp>n)
{
flag=1;
break;
}
}
for(int k=1;k<=i-j;k++)
{
if(flag) break;
temp*=m;
if(temp>n)
{
flag=1;
break;
}
}
if(flag)
{
ans=min(i*x+i*(m-1)*y+j*y,ans);
// cout<<i<<" "<<m<<" "<<j<<" "<<ans<<endl;
break;
}
}
}
write(ans);
return 0;
}
/*
*/
B. 星海浮沉录
水数据结构都不会了。。。,转化题意,维护每一个数的相邻出现位置的距离,找到最小的距离大于 \(x\) 的数,用 \(multiset\) 维护距离差,用线段树维护每个数的最大距离,复杂度 \(O((n+q)logn)\)
点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
#define mid (l+r>>1)
const int maxn=5e5+10;
using namespace std;
int n,q,a[maxn],G[maxn<<2],pos[maxn];
vector<int> l[maxn];
multiset<int> s[maxn];
void up(int id){G[id]=max(G[lid],G[rid]);}
void build(int id,int l,int r)
{
if(l==r) return G[id]=s[l].size()?*(--s[l].end()):0,void(0);
build(lid,l,mid),build(rid,mid+1,r); up(id);
}
void update(int id,int l,int r,int x)
{
if(l==r) return G[id]=s[l].size()?*(--s[l].end()):0,void(0);
x<=mid?update(lid,l,mid,x):update(rid,mid+1,r,x); up(id);
}
int query(int id,int l,int r,int x)
{
if(l==r) return l;
return G[lid]>x?query(lid,l,mid,x):query(rid,mid+1,r,x);
}
int main()
{
freopen("star.in","r",stdin);
freopen("star.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=0;i<=n;i++) l[i].push_back(0);
for(int i=1;i<=n;i++)
{
cin>>a[i];
l[a[i]].push_back(i);
pos[i]=l[a[i]].size()-1;
}
for(int i=0;i<=n;i++) l[i].push_back(n+1);
for(int i=0;i<=n;i++)
{
for(int j=0;j<l[i].size()-1;j++)
{
// cout<<l[i][j]<<" ";
s[i].insert(l[i][j+1]-l[i][j]);
}
}
build(1,0,n);
while(q--)
{
int op,x;
cin>>op>>x;
if(op==1)
{
if(a[x]==a[x+1]||x==n) continue;
s[a[x]].erase(s[a[x]].find(l[a[x]][pos[x]]-l[a[x]][pos[x]-1]));
s[a[x]].erase(s[a[x]].find(l[a[x]][pos[x]+1]-l[a[x]][pos[x]]));
s[a[x+1]].erase(s[a[x+1]].find(l[a[x+1]][pos[x+1]]-l[a[x+1]][pos[x+1]-1]));
s[a[x+1]].erase(s[a[x+1]].find(l[a[x+1]][pos[x+1]+1]-l[a[x+1]][pos[x+1]]));
l[a[x]][pos[x]]++,l[a[x+1]][pos[x+1]]--,swap(pos[x],pos[x+1]),swap(a[x],a[x+1]);
s[a[x]].insert(l[a[x]][pos[x]]-l[a[x]][pos[x]-1]);
s[a[x]].insert(l[a[x]][pos[x]+1]-l[a[x]][pos[x]]);
s[a[x+1]].insert(l[a[x+1]][pos[x+1]]-l[a[x+1]][pos[x+1]-1]);
s[a[x+1]].insert(l[a[x+1]][pos[x+1]+1]-l[a[x+1]][pos[x+1]]);
update(1,0,n,a[x]),update(1,0,n,a[x+1]);
}
else cout<<query(1,0,n,x)<<'\n';
}
return 0;
}
/*
7 6
0 1 0 2 1 0 3
2 2
2 3
2 4
1 4
2 3
2 4
*/
C. 勾指起誓
抽象题——45pts解法
以每一个人是否淘汰为状态,问题为边,在图上跑 \(dp\) ,共 \(2^m\) 个点,每个点 \(n\) 条出边,一个问题多次问是无意义的,所以可以看作每次等概率的选一个问题,求每个状态的概率。注意到有多个问题贡献同一条边,可以统一算。统计每条边上的问题数量 \(cnt_i\) , 再统计每个状态可以通过共多少个问题转移出去,记为 \(num_i\) ,考虑 \(dp\) 方程,设 \(f_i\) 表示有多少中问题排列方式可以转移到 \(i\) ,枚举转移的边 \(j\),\(f_{i \& j}+=f_{i}*cnt_{j}*A^{num_i-1-num_{i\&j}}_{num_i-1}\),指减去这个边的这个问题后,对 \(i\&j\) 无贡献的有 \(num_i-1-num_{i\&j}\) 条边,他们选出来后随机排列。
点击查看代码
#include<bits/stdc++.h>
const int mod=998244353;
const int maxn=2e5+10;
using namespace std;
int n,m,ans[25],cnt[(1<<21)+10],num[(1<<21)+10],f[(1<<21)+10],jc[maxn],ny[maxn];
string s;
int qpow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1) ans=1ll*ans*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ans;
}
void pre(int n)
{
jc[0]=ny[0]=1;
for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%mod;
ny[n]=qpow(jc[n],mod-2);
for(int i=n-1;i>=1;i--) ny[i]=1ll*ny[i+1]*(i+1)%mod;
}
int c(int n,int m)
{
return 1ll*jc[n]*ny[m]%mod;
}
int main()
{
freopen("yilihun.in","r",stdin);
freopen("yilihun.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
pre(n);
for(int i=1;i<=n;i++)
{
s.clear();
cin>>s,s=" "+s;
int k=0;
for(int j=m;j>=1;j--)if(s[j]=='1') k|=(1<<(m-j));
cnt[k]++;
}
for(int i=0;i<(1<<m);i++)
{
for(int j=0;j<(1<<m);j++)
{
if((i&j)&&(i&j)<i)
{
num[i]+=cnt[j];
}
}
}
f[(1<<m)-1]=c(n,num[(1<<m)-1]);
// cout<<num[(1<<m)-1]<<endl;
for(int i=(1<<m)-1;i>=0;i--)
{
if(f[i])
{
// cout<<i<<endl;
if(!num[i])
{
for(int j=0;j<m;j++)
{
if((1<<j)&i) ans[j+1]+=f[i],ans[j+1]%=mod;
}
}
else
{
for(int j=0;j<(1<<m);j++)
{
if(cnt[j]&&(i&j)&&(i&j)<i)
f[i&j]=(f[i&j]+1ll*f[i]*cnt[j]%mod*c(num[i]-1,num[i&j])%mod)%mod;
}
}
}
}
for(int i=1;i<=m;i++) cout<<ans[m-i+1]<<'\n';
return 0;
}
/*
*/
D. 第八交响曲
双调排序板子
点击查看代码
#include<bits/stdc++.h>
const int maxn=210;
using namespace std;
int n,m,pos[maxn][maxn],pos2[maxn];
vector<pair<int,int> >ans[maxn];
void solve(int l,int r,int dep,int now)
{
if(l==r) return ;
if(!pos[now][dep]) pos[now][dep]=++m;
int mid=l+r>>1;
for(int i=l,j=mid+1;j<=r;i++,j++)
ans[pos[now][dep]].push_back({i,j});
solve(l,mid,dep+1,now),solve(mid+1,r,dep+1,now);
}
void lsx(int l,int r,int dep)
{
if(l==r) return ;
int mid=l+r>>1;
lsx(l,mid,dep+1),lsx(mid+1,r,dep+1);
if(!pos2[dep]) pos2[dep]=++m;
for(int i=l,j=r;j>=mid+1;i++,j--)
ans[pos2[dep]].push_back({i,j});
solve(l,mid,dep+1,dep),solve(mid+1,r,dep+1,dep);
}
int main()
{
freopen("symphony.in","r",stdin);
freopen("symphony.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;int p=n;
n=pow(2,ceil(log2(n)));
lsx(1,n,1);
cout<<m<<'\n';
for(int i=1;i<=m;i++)
{
for(auto j:ans[i])
if(j.first<=p&&j.second<=p)
cout<<"CMPSWP R"<<j.first<<" R"<<j.second<<" ";
cout<<'\n';
}
return 0;
}
/*
*/