NOIP提高组模拟赛16
A. 茅山道术
简单,只考虑相邻两个同色的是否使用就行
code
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1000005;
const int mod=1e9+7;
int rem[maxn],c[maxn],las[maxn],pos[maxn],n;
bool vis[maxn];
int work(int x){
if(x>n)return 1;
if(vis[x])return rem[x];
vis[x]=1;
if(pos[x]&&pos[x]!=x+1)return rem[x]=(1ll*work(pos[x])+work(x+1))%mod;
return rem[x]=work(x+1);
}
int main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&c[i]);
for(int i=n;i>=1;--i){
pos[i]=las[c[i]];
las[c[i]]=i;
}
int aas=work(1);
printf("%d\n",aas);
return 0;
}
B. 泰拳警告
这题不难,如果你知道组合数的一些性质会更加简单,不会像我一样还要想垃圾
期望=概率*得分=总得分/总方案数
把平局看成种方案,这样算出总得分最后乘以的逆元即可
得分与平局数相关,考虑枚举平局数,假设我们知道剩下轮中,只考虑胜负,胜局多于负局的方案数
那么总得分为(可以在轮中任意轮平局,所以需要乘上)
剩下的问题变成求解
不妨求
当前这一轮只有两种情况胜或负
如果当前胜,那么需要知道前轮 胜场负场 的方案数
也就是 前轮 胜场等于负场 的方案数
如果是偶数那么后者的方案数就是,如果是奇数,那么不存在后者
如果当前负,那么需要知道前轮,胜场负场 的方案数
也就是 前轮 胜场 等于 负场 的方案数
如果是奇数那么后者的方案数就是,如果是偶数,那么不存在后者
这样就能解出了
code
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=3000005;
const int mod=998244353;
int jc[maxn],inv[maxn],n,p;
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 ycl(){
jc[0]=jc[1]=inv[0]=1;
for(int i=2;i<=n;++i)jc[i]=1ll*jc[i-1]*i%mod;
inv[n]=qpow(jc[n],mod-2);
for(int i=n-1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
int get_C(int n,int m){return 1ll*jc[n]*inv[n-m]%mod*inv[m]%mod;}
int f[maxn];
int main(){
freopen("fight.in","r",stdin);
freopen("fight.out","w",stdout);
scanf("%d%d",&n,&p);
ycl();
f[1]=f[2]=1;
for(int i=3;i<=n;++i){
f[i]=1ll*f[i-1]*2%mod;
if(i&1) f[i]=(1ll*f[i]+get_C(i-1,(i-1)>>1))%mod;
else f[i]=(1ll*f[i]-get_C(i-1,i>>1)+mod)%mod;
}
int ans=0,pk=1;
for(int k=0;k<=n;++k){
ans=(ans+1ll*get_C(n,k)*pk%mod*f[n-k]%mod*(k+1)%mod)%mod;
pk=1ll*pk*p%mod;
}
int in=qpow(p+2,n);
in=qpow(in,mod-2);
ans=1ll*ans*in%mod;
printf("%d\n",ans);
return 0;
}
C. 万猪拱塔
奇妙的线段树维护
发现符合要求的子矩阵一定是值域连续的,然后上题解。。。
考虑判定值在区间 内的所有数所在的格子是否形成了一个矩形,记这些
格子的颜色为黑色,其它的格子颜色为白色。
考虑所有的 个的小正方形 (部分超出边界也算),则所有
黑色格子形成一个矩形,当且仅当恰好有 个小正方形内部有 个黑色格子,
并且没有任何一个小正方形内部有 个黑色格子
从小到大枚举 ,对每个,记 表示染黑权值在 内的格子后,有多
少小正方形内部有个或个黑色格子
可以发现有 , ,于是只需要对每个 维护 最小值,最小值的
数目和取得最小值的所有 之和
每次 增加 时,会影响到周边的 个 的小正方形,在线段树上修改即可
时间复杂度
code
#include <cstring>
#include <cstdio>
#include<algorithm>
#include<unordered_map>
typedef long long ll;
using namespace std;
void swap(int &x,int &y){x^=y;y^=x;x^=y;}
ll min(ll x,ll y){return x<y?x:y;}
ll max(ll x,ll y){return x>y?x:y;}
const int maxn=200005;
const int mod=998244353;
unordered_map<ll,ll>mp[maxn];
struct node{ll cnt,lazy,sum,min;}ret;
int n,m;
struct tree{
node t[maxn<<2|1];
void built(int x ,int l,int r){
if(l==r){
t[x].cnt=1;
t[x].sum=l;
return;
}
int mid=(l+r)>>1;
built(x<<1,l,mid);
built(x<<1|1,mid+1,r);
}
void push_up(int x){
t[x].sum=t[x].cnt=0;
t[x].min=min(t[x<<1].min,t[x<<1|1].min);
if(t[x].min==t[x<<1].min)t[x].cnt+=t[x<<1].cnt,t[x].sum+=t[x<<1].sum;
if(t[x].min==t[x<<1|1].min)t[x].cnt+=t[x<<1|1].cnt,t[x].sum+=t[x<<1|1].sum;
}
void push_down(int x){
t[x<<1].lazy+=t[x].lazy;
t[x<<1|1].lazy+=t[x].lazy;
t[x<<1].min+=t[x].lazy;
t[x<<1|1].min+=t[x].lazy;
t[x].lazy=0;
}
void modify(int x,int l,int r,int L,int R,ll z){
if(L>R)return;
if(L<=l&&r<=R){
t[x].lazy+=z;
t[x].min+=z;
return;
}
int mid=(l+r)>>1;
if(t[x].lazy)push_down(x);
if(L<=mid)modify(x<<1,l,mid,L,R,z);
if(R>mid)modify(x<<1|1,mid+1,r,L,R,z);
push_up(x);
}
void query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R){
if(t[x].min==4){
ret.sum+=t[x].sum;
ret.cnt+=t[x].cnt;
}
return;
}
if(t[x].lazy)push_down(x);
int mid=(l+r)>>1;
if(L<=mid)query(x<<1,l,mid,L,R);
if(R>mid)query(x<<1|1,mid+1,r,L,R);
}
}T;
struct tpp{int x,y;}tp[maxn];
int ls[25];
void work(int x,int y,int w){
ll cnt=0,p,k=1;
ls[++cnt]=mp[x][y];
ls[++cnt]=mp[x-1][y-1];
ls[++cnt]=mp[x-1][y];
ls[++cnt]=mp[x][y-1];
sort(ls+1,ls+cnt+1);
p=lower_bound(ls+1,ls+cnt+1,w)-ls;//防止更新非法状态
for(p;p>=1;--p,k*=-1)T.modify(1,1,n,ls[p-1]+1,ls[p],k);
}
int main(){
freopen("pig.in","r",stdin);
freopen("pig.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=0;i<=n+1;++i)mp[0][i]=mp[n+1][i]=mp[i][0]=mp[i][n+1]=maxn;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
scanf("%d",&mp[i][j]);
tp[mp[i][j]].x=i;tp[mp[i][j]].y=j;
}
n=n*m;
T.built(1,1,n);
long long ans=0;
for(int i=1;i<=n;++i){
//对受影响的四个矩形进行操作
work(tp[i].x,tp[i].y,i);
work(tp[i].x+1,tp[i].y+1,i);
work(tp[i].x+1,tp[i].y,i);
work(tp[i].x,tp[i].y+1,i);
//查询当前贡献
T.query(1,1,n,1,i);
ans=(ans+(i+1)*ret.cnt-ret.sum+mod)%mod;
ret.cnt=ret.sum=0;
}
printf("%lld\n",ans);
return 0;
}
D. 抑郁刀法
不会。这题给我的唯一收获--了解了什么是问题
粘一下题解
考虑对图进行收缩,对于度数为 的点,可以直接将其删掉,并将最后答案乘
上
对于度数为 的点,记它连向的两个点分别为
将这个点删掉,若最后 之间同色,则有 种情况它与 都异色,有
种情况它与 都同色
若 之间异色,则有 种情况它与 都异色,有种情况与同色, 种情况与 同色
我们为每两个点之间记录分别表示它们同色时答案乘上的系数,异色时答案乘上的系数。
在删掉度数为的点时更新对应的 即可
最终得到的图中,每个点度数至少为 ,结合 可算出
对这个图做状压 ,并考虑上 的系数即可
状压 ,每次选择一个未染色的子集进行染色,时间复杂度
code
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】