NOIP模拟91(多校24)
T1 破门而入
解题思路
签到题(然而我数组开小直接变成暴力分。。。)
发现其实就是第一类斯特林数,然后 \(n^2\) 推就好了。
感觉可以用 NTT 优化成 \(nlogn\) ,但是好像并没有什么卵用(再说了,我也不会。。)
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e3+10,mod=998244353;
int n,m,ans,f[N][N];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
#undef int
int main()
{
#define int long long
freopen("broken.in","r",stdin); freopen("broken.out","w",stdout);
n=read(); m=read(); f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=min(i,m);j++)
f[i][j]=(f[i-1][j-1]+(i-1)*f[i-1][j]%mod)%mod;
for(int i=1;i<=m;i++) add(ans,f[n][i]); printf("%lld",ans);
return 0;
}
T2 翻转游戏
解题思路
比第一题还水,发现一个区间 \([l,r]\) 有贡献当且仅当 \(ch_l\neq ch_r\) 。
直接 \(\mathcal{O}(n)\) 扫一遍就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e6+10;
int n,ans=1,cnt[30];
char s[N];
#undef int
int main()
{
#define int long long
freopen("turn.in","r",stdin); freopen("turn.out","w",stdout);
scanf("%s",s+1); n=strlen(s+1);
for(int i=1;i<=n;i++) ans+=i-1-cnt[s[i]-'a'],cnt[s[i]-'a']++;
printf("%lld",ans);
return 0;
}
T3 奶油蛋糕塔
解题思路
可能这个题的做法比较多一些,壮压图论均可。
图论做法就是把四个不同的颜色作为四个点,那么每一块蛋糕就成了两个点之间的连边。
然后两条重边可以看做一条自环,那么我们枚举选择的点集问题就变成了在一个没有自环重边的图上选择边使得权值和最大。
如果是欧拉路那么都可以选,否则删去一条价值最小的边就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define count __builtin_popcount
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e5+10,INF=1e18;
int n,ans,all,val[4],id[100],mn[4][4],d[4][4],du[4];
struct Node{int x,y,val;} s[N];
void solve(int sta)
{
memset(d,0,sizeof(d)); memset(val,0,sizeof(val)); memset(du,0,sizeof(du));
for(int i=0;i<4;i++) for(int j=i+1;j<4;j++) mn[i][j]=INF;
int minn=INF,cnt=0,tot=0;
for(int i=1;i<=n;i++)
{
if( (((sta>>s[i].x)&1)^1) || (((sta>>s[i].y)&1)^1) ) continue;
if(s[i].x==s[i].y){val[s[i].x]+=s[i].val;continue;}
du[s[i].x]++; du[s[i].y]++; mn[s[i].x][s[i].y]=min(mn[s[i].x][s[i].y],s[i].val);
if(!d[s[i].x][s[i].y]){d[s[i].x][s[i].y]=s[i].val;continue;}
if((sta>>s[i].x)&1) val[s[i].x]+=s[i].val+d[s[i].x][s[i].y];
else val[s[i].y]+=s[i].val+d[s[i].x][s[i].y];
d[s[i].x][s[i].y]=0;
}
for(int i=0;i<4;i++) if(((sta>>i)&1) && !du[i] && count(sta)!=1) return ;
for(int i=0;i<4;i++) if((sta>>i)&1) cnt+=val[i];
for(int i=0;i<4;i++) for(int j=i+1;j<4;j++) cnt+=d[i][j];
for(int i=0;i<4;i++) tot+=du[i]&1;
if(tot==2||!tot) return ans=max(ans,cnt),void();
for(int i=0;i<4;i++) for(int j=i+1;j<4;j++)
if(du[i]!=1&&du[j]!=1) minn=min(minn,mn[i][j]);
if(minn!=INF) return ans=max(ans,cnt-minn),void();
}
#undef int
int main()
{
#define int long long
freopen("cake.in","r",stdin); freopen("cake.out","w",stdout);
n=read(); id['W']=0; id['X']=1; id['Y']=2; id['Z']=3;
for(int i=1,val;i<=n;i++)
{
s[i].val=read(),s[i].x=id[getchar()],getchar(),s[i].y=id[getchar()];
if(s[i].x>s[i].y) swap(s[i].x,s[i].y); all+=s[i].val;
}
for(int i=1;i<(1<<4);i++) solve(i);
printf("%lld",ans);
return 0;
}
T4 多重影分身之术
解题思路
二分答案 DP 均可(然而我考场上只想到了一个垃圾的 \(n^2\) 傻瓜 DP。。)
二分一个最小时间,对于一个点而言,如果左边有没有拾起来的卷轴,就应该向左走,可以先向左再向右或者先向右再向左。
对于一个左边没有卷轴的点直接向右走就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e5+10,INF=2e9;
int n,m,ans,s[N],p[N];
bool check(int tim)
{
int pos=1;
for(int i=1;i<=n;i++)
{
if(p[pos]<s[i])
{
if(s[i]-p[pos]>tim) return false;
int lim=max(s[i],max(tim-(s[i]-p[pos])+p[pos],(tim-s[i]+p[pos])/2+s[i]));
while(pos<=m&&p[pos]<=lim) pos++; continue;
}
while(pos<=m&&p[pos]<=s[i]+tim) pos++;
}
return pos>m;
}
#undef int
int main()
{
#define int long long
freopen("duplication.in","r",stdin); freopen("duplication.out","w",stdout);
n=read(); m=read(); s[n+1]=INF; p[m+1]=INF;
for(int i=1;i<=n;i++) s[i]=read(); sort(s+1,s+n+1);
for(int i=1;i<=m;i++) p[i]=read(); sort(p+1,p+m+1);
int l=0,r=INF; while(l<=r){int mid=(l+r)>>1;if(check(mid))r=mid-1,ans=mid;else l=mid+1;}
printf("%lld",ans);
return 0;
}