2021 10.12 模拟测试*
T1
Problem
Description
数轴上有 \(n\) 个棋子, 第 \(i\) 个棋子初始在 \(a_i\).
你会不断对棋子进行操作, 每次操作是选择一个棋子, 假设它的坐标是 \(x\), 则可以把它移动到 \(x - 1\) 或 \(x - 2\), 但要求移动后的位置原先没有棋子.
如果一个棋子的坐标变得小于等于 \(0\), 则称它挂掉了. 你需要求出有多少个排列 \(p\) 使得存在一种方案使得第 \(i\) 个挂掉的棋子是 \(p_i\). 答案对 \(10^9 + 7\) 取模.
Task
Input
第一行一个正整数 \(n\).
接下来一行 \(n\) 个正整数 \(a_i\).
Output
一行一个整数表示答案对 \(10^9 + 7\) 取模的结果.
Sample
Input
3
1 2 3
Output
4
Explanation
第 3 个棋子不能最先挂掉.
Constraints
对于所有数据, 保证 \(1 ≤ n ≤ 10^5, 1 ≤ a_1 < a_2 < · · · < a_n ≤ 10^9\).
• 子任务 1(10 分): 保证 \(n ≤ 3, a_i ≤ 10.\)
• 子任务 2(15 分): 保证 \(n ≤ 4, a_i ≤ 10.\)
• 子任务 3(20 分): 保证 \(n ≤ 5, a_i ≤ 10.\)
• 子任务 4(55 分): 无特殊限制.
Solution
对于任意一个数字,假设它前面有\(sum-1\)个数字,如果它满足可以在区间任意时刻跳出序列,那么需要满足在它的至少在第\(2 \times sum-1\)个位置,否则它是不可能跳出去的。
因此,我们可以顺序每一个数字,看它是否满足上述条件,如果无法满足,我们只要在当前序列随机删除一个数字即可(可以移动前面的数字让出通道,所以可以随机删),每删一个数字就有\(sum\)种情况,而这个是满足乘法性质的,所以对于\(ans\)直接乘上\(sum\)即可,最后得到序列则一定是满足全排列的,那么\(ans\)在乘上\(!sum\)就可以了
#include<bits/stdc++.h>
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int mod=1e9+7,N=1e5+5;
int n,ans=1,sum,a[N];
inline int add(int a,int b){return a+b>=mod?a+b%mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
int main()
{
#ifdef socc
freopen("soc.in","r",stdin);
#else
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
#endif
n=in;
for(int i=1;i<=n;i++)
{
sum++;
a[i]=in;
if(a[i]<dec(mul(sum,2),1)) ans=mul(ans,sum),sum--;
}
for(int i=1;i<=sum;i++) ans=mul(ans,i);
write(ans);
return 0;
}
T2
Problem
Description
有一个长度为 \(a + b + c + d + e\) 的 \(01\) 串, 它由 \(a\) 个 \(1\), \(b\) 个 \(0\), \(c\) 个 \(1\), \(d\) 个 \(0\), \(e\) 个 \(1\) 拼接起来组成.
有 m 个区间 \([l_i, r_i]\) 你可以进行若干次操作, 每次操作选择一个区间 \([l_i, r_i]\) 并将\([l_i, r_i]\)取反, 花费的代价为 \(r_i-l_i + 1\).
求将这个串变成全 \(1\) 串的最小代价. 无解输出 \(-1\).
Task
Input
第一行五个正整数 \(a, b, c, d, e.\)
第二行一个正整数 \(m.\)
接下来 \(m\) 行, 每行两个整数 \(l_i, r_i\)
Output
一行一个整数表示答案.
Sample
Input
1 2 3 4 5
3
2 3
2 6
4 10
Output
12
Explanation
操作 \([2, 6]\) 和 \([4, 10]\) 即可
Constraints
对于所有数据, 满足 \(1 ≤ a, b, c, d, e, m ≤ 10^5\), \(1 ≤ l_i < r_i ≤ a + b + c + d + e.\)
• 子任务 1(15 分): 保证 \(m ≤ 10.\)
• 子任务 2(50 分): 保证 \(a, b, c, d, e ≤ 50.\)
• 子任务 3(35 分): 无特殊限制.
Solution
这是一个最短路……
首先我们可以先把这个序列差分,差分后我们可以发现只有四个地方的值是不一样的,而对于每一次操作其实就是将操作区间两端的值互换,所以我们可以从区间右端点向左端点连一条大小为\(r-l+1\)的边,而答案则一定是分别覆盖两个端点的两条最短路,对于那四个点分别跑一个\(spfa\)就可以了。
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
#define in read()
using namespace std;
inline int read()
{
int data=0,w=1; char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
return data*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=5e5+10;
int x1,x2,x3,x4,x5,m,ans=inf;
struct edge{int v,w,nxt;}e[N];
int first[N],cnt;
int f[10][10],dis[N],vis[N];
inline void add(int u,int v,int w){e[++cnt]=(edge){v,w,first[u]};first[u]=cnt;}
void spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
queue<int>q;
dis[s]=0;vis[s]=1;q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();vis[u]=0;
for(int i=first[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w)
{
dis[v]=dis[u]+e[i].w;
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
}
signed main()
{
#ifdef socc
freopen("soc.in","r",stdin);
#else
//freopen("b.in","r",stdin);
//freopen("b.out","w",stdout);
#endif
x1=in,x2=in,x3=in,x4=in,x5=in,m=in;
int k1=x1+1,k2=x1+x2+1,k3=x1+x2+x3+1,k4=x1+x2+x3+x4+1;
for(int i=1;i<=m;++i)
{
int l=in,r=in;
add(l,r+1,r-l+1);
add(r+1,l,r-l+1);
}
spfa(k1);
f[1][2]=dis[k2];f[1][3]=dis[k3];f[1][4]=dis[k4];
spfa(k2);
f[2][3]=dis[k3];f[2][4]=dis[k4];
spfa(k3);
f[3][4]=dis[k4];
ans = min( f[1][2] + f[3][4] , min( f[1][3] + f[2][4] , f[1][4] + f[2][3] ) );
// cout<<f[1][2]<<'\n'<<f[1][3]<<'\n'<<f[1][4]<<'\n'<<f[2][3]<<'\n'<<f[2][4]<<'\n'<<f[3][4]<<'\n';
if(ans>=inf) write(-1);
else write(ans);
return 0;
}

浙公网安备 33010602011771号