A
题目描述:数轴上有n个棋子, 第i 个棋子初始在ai.你会不断对棋子进行操作, 每次操作是选择一个棋子, 假设它的坐标是x, 则可以把它移动到x−1或x−2, 但要求移动后的位置原先没有棋子.如果一个棋子的坐标变得小于等于0, 则称它挂掉了. 你需要求出有多少个排列p使得存在一种方案使得第i 个挂掉的棋子是pi. 答案对109+ 7取模.
解析
1.假如两个棋子位于101,103,和两个棋子位于1,3是没有区别的,可以101先挂掉,也可以以103先挂掉。
2.假如位于2,3,4,那么2,3就会堵住4,所以2,3必须挂掉一个。
3.假如位于1,3,5,那么3个数都可以畅通无阻,排列方式就是全排列 n ! n! n!(排列数公式)。
我们分析了以上3种情况,那么我们考虑维护一个序列1,3,5,7,…,2i-1,如果a[i]==2i,那么我们要将前面的一个挂掉,序列保持不变,如果a[i]>=2i-1,那么统计方案数。
for(int i=1;i<=n;i++)
{
ans=ans*i%mod;
}
求全排列,直接累乘。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=1e9+7;
int n,a[100005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
ll ans=1;
vector<int>b(1,1);//表示向量[1]==1
for(int i=2;i<=n;i++)
{
if(a[i]!=b.back()+1)//不在2*i的位置
{
b.push_back(b.back()+2);
}
else//在的话,那么挂掉前面的一个,至于哪一个,都可以
{
ans=ans*(b.size()+1)%mod;
}
}
for(int i=1;i<=b.size();i++)
{
ans=ans*i%mod;
}
cout<<ans;
return 0;
}
B
题目描述:有一个长度为a+b+c+d+e的01串, 它由a个1, b个0, c个1, d个0, e个1拼接起来组成.有m个区间[li,ri]. 你可以进行若干次操作, 每次操作选择一个区间[li,ri] 并将[li,ri] 取反, 花费的代价为ri −li+ 1.求将这个串变成全1串的最小代价. 无解输出−1.
解析
它是有5个区间组成,要将所有01串的都翻转太复杂了,不妨差分的思想,表示该位置和下一个位置的数字是否相同,相同为0,不同为1。例如a,b,c,d,e分别等于1,2,3,4,5。即100111000011111,差分后为101001000100000,对应了下标a,a+b,a+b+c,a+b+c+d。
重点: 如果我们要全部为1,即把差分的变成000000000000…,所以要让差分01串中的第1到第2的最短+第3到第4的最短,或者第1到第3+第2到第4,或者第1到第4+第2到第3,咦?这不就有点像最短路问题了吗,那就用这个思路,将这4个1作为点,每个取反的区间[l,r]为边,表示从l-1到r边权为r-l+1的边,来建立无向图,跑3遍dijstrar就解决了问题。
所以问题就转换到图上了 (太妙了)
#include<bits/stdc++.h>
using namespace std;
#define num ch-'0'
void get(int &res)
{
char ch;bool flag=0;
while(!isdigit(ch=getchar()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getchar