Codeforces Round #179 (Div. 2) DP + 组和 + 线段树 + 思维
A:
题意: 给出n个数,只能交换相邻两个数,问经过若干次交换后是否存在一个状态这个状态满足相邻两个数不同。
思路:只要判断存在一个数出现的次数小于等于(n +1)/2就满足条件
B:
题意:
给出两个长度为n的字符串,定义两个字符串不可比较:只要存在(1 ≤ i, j ≤ n), si > wi and sj < wj我们就说s,w是不可比较的。
字符串中可能会出现‘?'表示可取0-9的任意数,问存在多少种可能使得两个串不可比较。
思路:
我觉得属于黑书里面分决策动机的DP,我再考虑的时候,想到面对当前i子符,状态什么? 就是比较到了第i个字符,是否出现了si >wi sj < wj的情况,于是状态就出来了,想到状态然后再根据决策推递推式就好了,这里我觉得没什么组和吧,就是找出大于小于的。
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 100015 using namespace std; const int inf = 0x7f7fffff; const ll mod = 1000000007; ll dp[N][2][2];//到底i个字符串,是否出现s > w ,是都出现s < w bool vt[N][2][2]; char s[N],w[N]; int main() { // Read(); int i,j,n,k; scanf("%d",&n); scanf("%s%s",s + 1,w + 1); CL(dp,0); CL(vt,false); dp[0][0][0] = 1; vt[0][0][0] = true; for (i = 0; i < n; ++i) { for (j = 0; j < 2; ++j) { for (k = 0; k < 2; ++k) { if (vt[i][j][k]) { if (s[i + 1] == '?' && w[i + 1] != '?') { //s比w大 if ((9 - (w[i + 1] - '0')) > 0) { dp[i + 1][(j|1)][k] += dp[i][j][k]*(9 - (w[i + 1] - '0')); dp[i + 1][(j|1)][k] %= mod; vt[i + 1][(j|1)][k] = true; } //s比w小 if ((w[i + 1] - '0') > 0) { dp[i + 1][j][(k|1)] += dp[i][j][k]*(w[i + 1] - '0'); vt[i + 1][j][(k|1)] = true; dp[i + 1][j][(k|1)] %= mod; } dp[i + 1][j][k] += dp[i][j][k]; vt[i + 1][j][k] = true; dp[i + 1][j][k] %= mod; } else if (s[i + 1] != '?' && w[i + 1] == '?') { if ((s[i + 1] - '0') > 0) { dp[i + 1][(j|1)][k] += dp[i][j][k]*(s[i + 1] - '0'); dp[i + 1][(j|1)][k] %= mod; vt[i + 1][(j|1)][k] = true; } if ((9 - (s[i + 1] - '0')) > 0) { dp[i + 1][j][(k|1)] += dp[i][j][k]*(9 - (s[i + 1] - '0')); vt[i + 1][j][(k|1)] = true; dp[i + 1][j][(k|1)] %= mod; } dp[i + 1][j][k] += dp[i][j][k]; vt[i + 1][j][k] = true; dp[i + 1][j][k] %= mod; } else if (s[i + 1] != '?' && w[i + 1] != '?') { if (s[i + 1] > w[i + 1]) { dp[i + 1][(j|1)][k] += dp[i][j][k]; vt[i + 1][(j|1)][k] = true; dp[i + 1][(j|1)][k] %= mod; } else if (s[i + 1] < w[i + 1]) { dp[i + 1][j][(k|1)] += dp[i][j][k]; vt[i + 1][j][(k|1)] = true; dp[i + 1][j][(k|1)] %= mod; } else if (s[i + 1] == w[i + 1]) { dp[i + 1][j][k] += dp[i][j][k]; vt[i + 1][j][k] = true; dp[i + 1][j][k] %= mod; } } else if (s[i + 1] == '?' && w[i + 1] == '?') { dp[i + 1][(j|1)][k] += dp[i][j][k]*45; vt[i + 1][(j|1)][k] = true; dp[i + 1][(j|1)][k] %= mod; dp[i + 1][j][(k|1)] += dp[i][j][k]*45; vt[i + 1][j][(k|1)] = true; dp[i + 1][j][(k|1)] %= mod; dp[i + 1][j][k] += dp[i][j][k]*10; vt[i + 1][j][k] = true; dp[i + 1][j][k] %= mod; } } } } } cout<<dp[n][1][1]<<endl; return 0; }
C:
题意:
给你n个数,m个操作,每个操作为 li, ri, di 表示将[a[li],a[ri]] 都加上 di, 然后是k个询问,xi,yi表示之星xi~yi的操作。问最后数组是什么
思路:
两颗线段树,一个维护每个操作进行了多少次,一个维护整个数组,MD传至把ll改成了int最后错了,悲剧啊。
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define sz(a) int((a).size()) #define pb push_back #define all(c) (c).begin(),(c).end() #define tr(c,i) for(typeof((c).begin() i = (c).begin(); i != (c).end(); i++) #define M 137 #define N 100006 using namespace std; struct node { int l,r; int d; }nd[N]; int v1[4*N]; ll v2[4*N]; int a[N]; void pushdown(int rt,int mk) { if (mk == 1) { if (v1[rt]) { v1[rt<<1] += v1[rt]; v1[rt<<1|1] += v1[rt]; v1[rt] = 0; } } else { if (v2[rt]) { v2[rt<<1] += v2[rt]; v2[rt<<1|1] += v2[rt]; v2[rt] = 0; } } } void build1(int l,int r,int rt) { v1[rt] = 0; if (l == r) { return ; } int m = (l + r)>>1; build1(l,m,rt<<1); build1(m + 1,r,rt<<1|1); } void build2(int l,int r,int rt) { v2[rt] = 0; if (l == r) { v2[rt] = a[l]; return ; } int m = (l + r)>>1; build2(l,m,rt<<1); build2(m + 1,r,rt<<1|1); } void update1(int L,int R,int sc,int l,int r,int rt) { if (l >= L && r <= R) { v1[rt] += sc; return ; } pushdown(rt,1); int m = (l + r)>>1; if (L <= m) update1(L,R,sc,l,m,rt<<1); if (R > m) update1(L,R,sc,m + 1,r,rt<<1|1); } void update2(int L,int R,ll sc,int l,int r,int rt) { if (l >= L && r <= R) { v2[rt] += (ll)sc; return ; } pushdown(rt,2); int m = (l + r)>>1; if (L <= m) update2(L,R,sc,l,m,rt<<1); if (R > m) update2(L,R,sc,m + 1,r,rt<<1|1); } int query1(int pos,int l,int r,int rt) { if (l == r) { return v1[rt]; } pushdown(rt,1); int res = 0; int m = (l + r)>>1; if (pos <= m) res = query1(pos,l,m,rt<<1); else res = query1(pos,m + 1,r,rt<<1|1); return res; } void query2(int l,int r,int rt) { if (l == r) { printf("%I64d ",v2[rt]); return; } pushdown(rt,2); int m = (l + r)/2; query2(l,m,rt<<1); query2(m + 1,r,rt<<1|1); } int main() { int i; int n,m,k; scanf("%d%d%d",&n,&m,&k); for (i = 1; i <= n; ++i) scanf("%d",&a[i]); for (i = 1; i <= m; ++i) { scanf("%d%d%d",&nd[i].l,&nd[i].r,&nd[i].d); } build1(1,m,1); for (i = 0; i < k; ++i) { int x,y; scanf("%d%d",&x,&y); update1(x,y,1,1,m,1); } build2(1,n,1); for (i = 1; i <= m; ++i) { int no = query1(i,1,m,1); if (no == 0) continue; update2(nd[i].l,nd[i].r,(ll)nd[i].d*no,1,n,1); } query2(1,n,1); printf("\n"); return 0; }
D:
题意:
给出一个邻接矩阵,表示每个点之间的距离,然后每次删除一个点,求在删除该点前,这个图中任意两点之间的最短距离的和。
思路:
这里关键是理解好floyd算法的动态规划的由来dp[i][j][k] = dp[i][k][k - 1] + dp[k][j][k -1] ,dp[i][j][k]表示i到j的最短距离中间经过的点的索引小于等于k
然后这里我们倒着每次加进来一个点,就表示前边是索引 小于的等于x - 1求出的任意两点之间的距离,然后只要O(N^2)求出任意两点之间中间经过的索引小于等于x的最短距离就好了,的那个x = n是就表示了整个图中任意两点之间的最短距离了。
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 517 using namespace std; const int inf = 0x7f7fffff; const int mod = 1000000007; int mat[N][N]; int v[N]; ll ans[N]; int ln; int main() { int n,i,j,k; scanf("%d",&n); for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { scanf("%d",&mat[i][j]); } } for (i = 1; i <= n; ++i) scanf("%d",&v[i]); reverse(v + 1, v + 1 + n); // for (i = 1; i <= n; ++i) printf("%d\n",v[i]); ln = 0; for (k = 1; k <= n; ++k) { // printf(">>%d\n",k); int ve = v[k]; for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) { mat[i][j] = min(mat[i][j],mat[i][ve] + mat[ve][j]); } } ll sum = 0; for (i = 1; i <= k; ++i) { for (j = i + 1; j <= k; ++j) { sum += mat[v[i]][v[j]] + mat[v[j]][v[i]]; } } ans[ln++] = sum; } for (i = ln - 1; i > 0; --i) printf("%I64d ",ans[i]); printf("%I64d\n",ans[0]); return 0; }
E:
题意:
有n个人过河,只有一个小船,每次小船只能载总重量为k的人的个数,这n个人的体重要么是50kg,要么是100kg。然后过河,这里保证不能超出船的载重,并且保证每次都有一个人在驾驶船。 这里顶多会有4*n步(考虑两个过去,一个回来,换一个大的过去,回来小的这种最坏情况)
思路:
组和+DP (假设从左边到右边)
首先考虑当前的状态,dp[i][j][k]表示第k步后左边还有i个50j个100,然后考虑每一步走奇数步时是从左往右走,偶数步时是从右往左走。然后推出每一步合法的状态的步数。
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll __int64 #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define M 137 #define N 55 using namespace std; const int inf = 0x7f7fffff; const ll mod = 1000000007; ll dp[N][N][M]; ll c[N][N]; bool vt[N][N][M]; void init() { int i,j; for (i = 0; i < N; ++i) c[i][i] = c[i][0] = 1; for (i = 2; i < N; ++i) { for (j = 1; j < i; ++j) { c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; c[i][j] %= mod; } } } int main() { // Read(); int i,j,k; int n,m; init(); scanf("%d%d",&n,&m); int n50 = 0; int n10 = 0; int x = 0; int y = 0; for (i = 0; i < n; ++i) { scanf("%d",&x); if (x == 50) n50++; else n10++; } CL(dp,0); CL(vt,false); dp[n50][n10][0] = 1; vt[n50][n10][0] = true; for (k = 0; k <= n*4; ++k) { for (i = 0; i <= n50; ++i) { for (j = 0; j <= n10; ++j) { if (vt[i][j][k]) { //从左往右走 if ((k + 1)%2 == 1) { for (x = 0; x <= i; ++x) { for (y = 0; y <= j; ++y) { if ((x == 0 && y == 0) || (x*50 + y*100 > m)) continue; dp[i - x][j - y][k + 1] += dp[i][j][k]*c[i][x]%mod*c[j][y]%mod; vt[i - x][j - y][k + 1] = true; dp[i - x][j - y][k + 1] %= mod; } } }//从右往左走 else { int tn = n50 - i; int tm = n10 - j; for (x = 0; x <= tn; ++x) { for (y = 0; y <= tm; ++y) { if ((x == 0 && y == 0) || (x*50 + y*100 > m)) continue; dp[i + x][j + y][k + 1] += dp[i][j][k]*c[tn][x]%mod*c[tm][y]%mod; vt[i + x][j + y][k + 1] = true; dp[i + x][j + y][k + 1] %= mod; } } } } } } } for (k = 1; k <= 4*n; k++) { if (vt[0][0][k]) { break; } } if (k <= 4*n) { printf("%d\n",k); printf("%I64d\n",dp[0][0][k]); } else { printf("-1\n0\n"); } return 0; }