2023.3.2 日寄
\(~~~~\) 希望这不是退役前最后一篇日寄,毕竟明天是不可能写日寄的。
一言
\(~~~~\) 我的北京到了,你的伦敦也到了,浮天沧海远,万里眼中明——我煮酒,等着你回来赋诗。——简媜
杂题选做
A Perfect Problem
题意
\(~~~~\) 定义一个序列是完美的当且仅当它的所有非空子序列 \(\{a_n\}\)满足:\(min\{a_n\} \times max\{a_n\} \geq \sum a_i\)。求长为 \(n\) ,值域在 \([1,n]\) 的序列有多少是完美的。
\(~~~~\) \(1\leq n\leq 200\).
题解
\(~~~~\) 来找一些性质,比如说:我们把原序列排序,那么排序后的所有子区间合法等价于原来的所有子序列合法。因为显然去掉子区间非端点的部分和那和变小但是最值的积不变。
\(~~~~\) 进一步地,我们发现左端点可以往左移到 \(1\),那也就是所有前缀合法即可,原因也显然。
\(~~~~\) 那现在我们就有了 \(\mathcal{O(n)}\) 判定的方法,也就有了 \(dp\) 的雏形:\(dp_{i,j}\) 表示当前考虑数字 \(i\) 放到序列的第 \(j\) 个数,后面可以再附带上当前的和,然后你就获得了优秀的 \(\mathcal{O(n^5)}\) dp.
\(~~~~\) 对于值域来做点优化,我们发现第 \(n\) 位最终的值要么是 \(n\) 要么是 \(n+1\) ,且为 \(n\) 的时候前面全是 \(n\)。同理你也会发现第 \(i\) 位的值 \(\geq i\) ,证明:若 \(a_i< i\) ,则 \(a_1\times a_i<a_1\times i=\sum_{j=1}^i a_1\leq \sum_{j=1}^i a_j\) ,显然不满足题意。
\(~~~~\) 进一步地,我们打表还会发现 \(a_i\) 只能取 \(n+1-\sqrt{n}\) 级别以上的数。结合以上一堆结论我们直接记搜就好了。
代码
查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
int n,MOD,Fac[505],Inv[505];
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
int ret=1;
while(b)
{
if(b&1) ret=Mul(ret,a);
b>>=1;a=Mul(a,a);
}
return ret;
}
int A1;
int dp[505][505][20];
int dfs(int Num,int Sum,int Val,int Up)
{
if(~dp[Num][Sum][Val]) return dp[Num][Sum][Val];
int &res=dp[Num][Sum][Val]; res=0;
if(Val<=Up-1&&Num<Val) return 0;
if(Val==Up) return res=Num<n?Inv[n-Num]:0;
int x=Up-Val;
for(int j=0;Num+j<=n&&Sum+j*x<=A1;j++) res=Add(res,Mul(Inv[j],dfs(Num+j,Sum+j*x,Val+1,Up)));
return res;
}
int main() {
read(n);read(MOD);
Fac[0]=1;
for(int i=1;i<=n;i++) Fac[i]=Mul(Fac[i-1],i);
Inv[n]=qpow(Fac[n],MOD-2);
for(int i=n-1;i>=0;i--) Inv[i]=Mul(Inv[i+1],i+1);
int Ans=0;
for(int Del=0;Del<=18;Del++)
{
memset(dp,-1,sizeof(dp));
A1=n+1-Del;
Ans=Add(Ans,dfs(0,0,0,Del));
}
printf("%d",Mul(Ans,Fac[n]));
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
Tourist Reform
题意
\(~~~~\) 给一张初始联通的无向图的边定向,使得 \(r_i\) 的最小值最大。其中 \(r_i\) 指的是从 \(i\) 出发能到达的点数。要求给出点数和构造方案。
\(~~~~\) \(1\leq n\leq 4\times 10^5,1\leq m\leq 4\times 10^5\).
题解
\(~~~~\) 我们来考虑这题SPJ怎么写?是不是先Tarjan缩点,然后我们发现最小值一定取自那些缩点后没有出边的点。
\(~~~~\) 那反过来我们最大化这个值即可。注意到无向图中边双一定能被定向为强联通分量,那我们把原图先边双缩点成一棵树,然后树上的边必然贡献 \(tot-1\) 个出边,所以我们只能让那个点数最多的点双成为答案,那以它为根往下搜,让边都指向根即可。
\(~~~~\) 边双内怎么定向?注意到无向图的dfs树上没有横叉边,那以任何一个点为根,树边都指向叶子,返租边都指向祖先即可。
代码
查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
bool vis[400005];
int dfn[400005],low[400005],Times,tot,Siz[400005],belong[400005];
stack<int>S;
struct Edge{
int u,v,dir;
Edge(){dir=0;}
Edge(int U,int V){u=U,v=V;}
}E[400005];
vector<PII>G[400005];
void Tarjan(int u)
{
dfn[u]=low[u]=++Times;S.push(u);
for(int i=0;i<(int)G[u].size();i++)
{
int v=G[u][i].first,id=G[u][i].second;
if(vis[id]) continue; vis[id]=true;
if(!dfn[v]) Tarjan(v);
low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
tot++;int t=-1;
while(t!=u)
{
t=S.top();S.pop();
belong[t]=tot;
Siz[tot]++;
}
}
}
void dfs(int u,int fa)
{
for(int i=0;i<(int)G[u].size();i++)
{
int v=G[u][i].first,id=G[u][i].second;
if(v==fa) continue;
E[id].dir=(belong[E[id].u]==v?1:-1);
dfs(v,u);
}
}
int dep[400005];
void dfs2(int u,int fa)
{
dep[u]=dep[fa]+1; vis[u]=true;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first,id=G[u][i].second;
if(v==fa||belong[v]!=belong[u]) continue;
if(!dep[v]) E[id].dir=(v==E[id].u?-1:1),dfs2(v,u);
else if(dep[v]<dep[u]) E[id].dir=(u==E[id].u?1:-1);
}
}
int main() {
int n,m;read(n);read(m);
for(int i=1,u,v;i<=m;i++)
{
read(u);read(v);
G[u].push_back(mp(v,i));
G[v].push_back(mp(u,i));
E[i]=Edge(u,v);
}
for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=m;i++) vis[i]=0;
int rt=0;
for(int i=1;i<=tot;i++) if(Siz[rt]<Siz[i]) rt=i;
printf("%d\n",Siz[rt]);
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1;i<=n;i++)
{
if(!vis[i]) dfs2(i,0);
}
for(int i=1;i<=tot;i++) G[i].clear();
for(int i=1;i<=m;i++)
{
int u=E[i].u,v=E[i].v;
if(belong[u]!=belong[v]) G[belong[u]].push_back(mp(belong[v],i)),G[belong[v]].push_back(mp(belong[u],i));
}
dfs(rt,0);
for(int i=1;i<=m;i++)
{
if(E[i].dir==1) printf("%d %d\n",E[i].u,E[i].v);
else printf("%d %d\n",E[i].v,E[i].u);
assert(E[i].dir==1||E[i].dir==-1);
}
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
Shik and Copying String
题意
\(~~~~\) 给出字符串 \(S_0\) 和 \(T\),\(S_{i,j}\) 要么是 \(S_{i-1,j}\) 要么是 \(S_{i,j-1}\)。求使得 \(S_k=T\) 的最小的 \(k\).
\(~~~~\) \(1\leq |S_0|\leq 10^6\).
题解
\(~~~~\) 我们发现每个位置一定是由上个部分画一条折线。与前面的折线不交覆盖到当前位置所需的最小层数。
\(~~~~\) 那我们倒着来,找到每一段由原字符串的哪个字符作为起点,同时记录已有的折线是否干扰到当前折线,也就是记录前面的已有的折点,如果它到了当前的位置那就必须再保留。
\(~~~~\) 不过还是代码好理解一些。
代码
查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
int n,Q[1000005];
char S[1000005],T[1000005];
bool Equal(char A[],char B[])
{
for(int i=1;i<=n;i++) if(A[i]!=B[i]) return false;
return true;
}
int main() {
read(n);
scanf("%s %s",S+1,T+1);
if(Equal(S,T)) return puts("0")&0;
int pos=n,Head=1,Tail=0,Ans=0;
for(int i=n;i>=1;i--)
{
if(T[i]==T[i-1]) continue;
pos=min(pos,i);
while(pos>=1&&S[pos]!=T[i]) pos--;
if(!pos) return puts("-1")&0;
while(Head<=Tail)
{
if(Q[Head]-(Tail-Head+1)+1>i) Head++;
else break;
}
Q[++Tail]=pos;
if(i!=pos) Ans=max(Ans,Tail-Head+1);
}
printf("%d",Ans+1);
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
Game of AI (easy version)
题意
\(~~~~\) \(n\) 个 bot.初始时第 \(i\) 个 bot 占领了第 \(i\) 座塔。接下来会有一个排列 \(p\) 和一个序列 \(a\)。bot 将按照 \(p\) 中的顺序行动,若当前行动的 bot 有塔,则会攻击 \(a_{p_i}\) 这座塔,赶走那里的bot。最后 \(b\) 序列记录了每座塔被哪个bot占领。求可能的 \((a,b)\) 的数量。
\(~~~~\) \(1\leq n\leq 5000\).
题解
\(~~~~\) 我们发现若最终 \(b_i \not = i\) ,那就说明 \(b_i\) 的目标必然是 \(i\),也就是 \(a_{b_i}=i\)。否则 \(b_i=i\) ,此时对于所有 \(a_j=i\) 的 \(j\) 应该有 \(b_j \not = j\),这样才能使得 \(j\) 先被占领,可以安排合适的顺序。
\(~~~~\) 那么对于 \(b_i \not = i\) 的那些位置,我们连接一条 \(i\) 到 \(b_i\) 的边,那我们就可以找到很多有趣的东西。比如说最后的图一定是若干条链,因为成环了的话那就不可能有解了。链上有入边的点的 \(a_i\) 已经确定了,只需要考虑链头的 \(a_i\) 分配。而对于链我们分成长度为 \(2\) (结点数为 \(2\)) 和非 \(2\) ,前一类我们的链头的 \(a_i\) 只能指向非链尾的位置,这样才能安排顺序被取代这个bot占领的塔。而后一类的链头我们就可以随便把 \(a_i\) 安排成非自身的,因为它可以被任意安排顺序取代。
\(~~~~\) 用组合数算一算就好了。
代码
查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
int MOD,Fac[5005],Inv[5005],Pow[5005][5005];
inline int Add(int a,int b){a+=b;return a>=MOD?a-MOD:a;}
inline int Dec(int a,int b){a-=b;return a<0?a:a+MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
int ret=1;
while(b)
{
if(b&1) ret=Mul(ret,a);
b>>=1;a=Mul(a,a);
}
return ret;
}
inline int C(int n,int r){return n<r?0:Mul(Fac[n],Mul(Inv[r],Inv[n-r]));}
int main() {
int n;read(n);read(MOD);
Fac[0]=1;
for(int i=1;i<=5000;i++) Fac[i]=Mul(Fac[i-1],i);
Inv[5000]=qpow(Fac[5000],MOD-2);
for(int i=4999;i>=0;i--) Inv[i]=Mul(Inv[i+1],i+1);
for(int i=1;i<=5000;i++)
{
Pow[i][0]=1;
for(int j=1;j<=5000;j++) Pow[i][j]=Mul(Pow[i][j-1],i);
}
int Ans=0;
for(int i=1;i*2<=n;i++)
for(int j=0;j+i*2<=n;j++)
Ans=Add(Ans,Mul(C(n,j),Mul(Fac[n-j],Mul(Mul(Pow[n-1][i],Pow[n-i-j][j]),Mul(C(n-i-j-1,i-1),Inv[i])))));
printf("%d",Ans);
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
鲜花
\(~~~~\) 今天感觉是很摆的一天,但我希望不是可以摆的最后一天/kk
\(~~~~\) 上午做了一道胡了两道没写,下午也没写代码然后 4:10 搬到下面机房过后直接开摆指导sha老师做题。
\(~~~~\) 晚上想了上面写的最后这道题easy version,hard version弃了然后就来写日寄了。
\(~~~~\) 总结:模拟赛是第一生产力。
标签:ch,int,pos,2023.3,leq,while,getchar From: https://www.cnblogs.com/Azazel/p/17173872.html