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());res=res*10+num);
(flag)&&(res=-res);
}
#define N 1000005
#define ll long long
int a,b,c,d,e,m;
ll edge[10][10];
ll dis[N],sta;
bool vis[N];
int first[N],nex[N],to[N],w[N],tot;
inline void add(int x,int y,int z)
{
nex[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
inline void dij(int sta)
{
priority_queue<pair<int,int> >q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[sta]=0;
q.push(make_pair(0,sta));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
else vis[x]=1;
for(int i=first[x];i;i=nex[i])
{
int y=to[i];
if(dis[y]>dis[x]+w[i])
{
dis[y]=dis[x]+w[i];
q.push(make_pair(-dis[y],y));
}
}
}
}
int main()
{
get(a);get(b);get(c);get(d);get(e);get(m);
for(int i=1;i<=m;i++)
{
int l,r;
get(l);get(r);
add(l-1,r,r-l+1);
add(r,l-1,r-l+1);
}
dij(a);
edge[1][2]=dis[a+b];
edge[1][3]=dis[a+b+c];
edge[1][4]=dis[a+b+c+d];
dij(a+b);
edge[2][3]=dis[a+b+c];
edge[2][4]=dis[a+b+c+d];
dij(a+b+c);
edge[3][4]=dis[a+b+c+d];
ll ans=0x3f3f3f3f3f;
ans=min(edge[1][2]+edge[3][4],min(edge[1][3]+edge[2][4],edge[1][4]+edge[2][3]));
if(ans!=0x3f3f3f3f3f) cout<<ans;
else cout<<-1;
return 0;
}
C
第一行三个正整数n,m, k.
接下来m行, 每行三个整数ui,vi,wi 表示一条连接(ui,vi), 长度为wi 的边.
对于所有数据, 满足2≤n≤106,1≤m,k≤106,min(n,m, k)≤5,1≤wi ≤108
首先观察数据,min(n,m,k)<=5
显然如果n取最小,那么n<k,不可能;如果m取最小,那么m<k,也不可以。
那么我们只需讨论k<5的情况了
我们必须要分5种类讨论吗?
不一定。
我们只需在n这个点的后面连上边权为0的点n+1,n+2…
我们就都转化为了k==5的情况了
那又要怎么搞呢?
5=2+2+1,我们可以枚举到1经过边的数量为2的点和到n边的数量为2的点,中间再枚举边,把它们连起来更新答案。至于从1开始的2条边和从n开始的2条边中,我们只需要找前3小的边就可以了。
下面是std代码PS:我还没写完
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x1f3f3f3f;
int main() {
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, k;
cin >> n >> m >> k;
if (k >= n || k > m) {
cout << -1 << "\n";
return 0;
}
vector<vector<pair<int, int>>> g(n + 5 - k);
for (int i = 0; i < m; ++i) {
int v, u, w;
cin >> v >> u >> w;
--v; --u;
g[v].emplace_back(u, w);
g[u].emplace_back(v, w);
}
for (int i = 0; i < 5 - k; ++i) {
g[n + i - 1].emplace_back(n + i, 0);
g[n + i].emplace_back(n + i - 1, 0);
}
n += 5 - k;
auto init = [&](int v) {
vector<vector<pair<int, int>>> dist(n, vector<pair<int, int>>(3, make_pair(INF, -1)));
for (auto e1 : g[v]) {
int u1 = e1.first, w1 = e1.second;
if (u1 == 0 || u1 == n - 1) {
continue;
}
for (auto e2 : g[u1]) {
int u2 = e2.first, w2 = e2.second;
if (u2 == 0 || u2 == n - 1) {
continue;
}
pair<int, int> r(w1 + w2, u1);
if (r < dist[u2][0]) {
dist[u2][2] = dist[u2][1];
dist[u2][1] = dist[u2][0];
dist[u2][0] = r;
} else if (r < dist[u2][1]) {
dist[u2][2] = dist[u2][1];
dist[u2][1] = r;
} else if (r < dist[u2][2]) {
dist[u2][2] = r;
}
}
}
return dist;
};
vector<vector<pair<int, int>>> foo = init(0);
vector<vector<pair<int, int>>> bar = init(n - 1);
int ans = INF;
for (int v = 1; v < n - 1; ++v) {
for (auto e : g[v]) {
int u = e.first, w = e.second;
if (u == 0 || u == n - 1) {
continue;
}
int pv = 0, pu = 0;
if (foo[v][pv].second == u) {
++pv;
}
if (bar[u][pu].second == v) {
++pu;
}
if (foo[v][pv].second != bar[u][pu].second) {
ans = min(ans, foo[v][pv].first + bar[u][pu].first + w);
} else {
int qv = pv + 1, qu = pu + 1;
if (foo[v][qv].second == u) {
++qv;
}
if (bar[u][qu].second == v) {
++qu;
}
ans = min(ans, foo[v][pv].first + bar[u][qu].first + w);
ans = min(ans, foo[v][qv].first + bar[u][pu].first + w);
}
}
}
cout << (ans == INF ? -1 : ans) << "\n";
return 0;
}
.