Codeforces Round #199 (Div. 2)
题意:给定N个数,每个数的取值范围为1-7,N是3的倍数,判定是否能够恰好将N个数分成若干三元组,使得一个组中的元素a,b,c满足 a < b < c 并且 a|b && b|c(整除)。
分析:满足这种条件的三元组只有[1, 2, 4], [1, 2, 6], [1, 3, 6]因此如果有5或者是7肯定是不行的,然后是1的个数要等于4和6的个数之和,最后就是2的数量必须4的数量。
#include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 100005; int n; int cnt[10]; int main() { int x; bool flag = false; scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%d", &x); if (x == 5 || x == 7) flag = true; cnt[x]++; } if (flag || cnt[1] != cnt[4]+cnt[6] || cnt[2] < cnt[4]) { puts("-1"); return 0; } for (int i = 0; i < cnt[4]; ++i) puts("1 2 4"); for (int i = 0; i < cnt[3]; ++i) puts("1 3 6"); for (int i = 0; i < cnt[6]-cnt[3]; ++i) puts("1 2 6"); return 0; }
题意:有N个人站成一行,现在有一个情报要从编号S传到编号F,每次每个人只能够向左或者向右边的一个人传送。给定一个时间表,某一段时间内某个区间的人使不能够接收和传送情报,要求输出花最小时间的代价的传送方案。
分析:可以得到一个结论,如果S < F,那么情报不可能向左边传,顶多是停在某个人手中,S > F 同理,因此直接模拟即可。
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int M = 100005; int n, m, s, f; struct interval { int l, r, ti; void read() { scanf("%d %d %d", &ti, &l, &r); } bool judge(int x) { if (x >= l && x <= r) return false; else return true; } }q[M]; void solve() { int dir = s < f ? 1 : -1; char ch = s < f ? 'R' : 'L'; int ti = 0; int X = 0; while (s != f) { ++ti; if (ti == q[X].ti) { if (q[X].judge(s) && q[X].judge(s+dir)) putchar(ch), s += dir; else putchar('X'); X++; } else { putchar(ch); s += dir; } } } int main() { scanf("%d %d %d %d", &n, &m, &s, &f); for (int i = 0; i < m; ++i) { q[i].read(); } solve(); return 0; }
题意:问一个橱柜中能够放置多少个球。
分析:其实就是一个分段函数,可以分别在顶端放1个,放2个,放3个的h取值。
#include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; int r, h; const double eps = 1e-10; int sign(double x) { return x < -eps ? -1 : x > eps ? 1 : 0; } int get(double x) { if (sign(2*x - sqrt(3)*r) >= 0) return 3; else if (sign(2*x - r) >= 0) return 2; else return 1; } int main() { scanf("%d %d", &r, &h); printf("%d\n", h/r*2 + get(h-h/r*r)); return 0; }
题意:给定一个3*N的棋盘,其中某些位置不能够放置,并且有一个点是空的,问使用1*2的方格来平铺能够有多少种方案,要求一个空格周围有1*2的方格可能移动。
分析:与其他的题目稍有不同的就是棋盘多了一些限制并且有一个点周围的点至少有一个1*2的方格能够移动。对于前者,在dp的时候做个位运算即可,后者我的做法是用总的方案数减去不合法的方案数。
#include <cstdlib> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int mod = int(1e9)+7; vector<int>vt[8]; int dp[10005][8]; int mp[10005]; int ocp[10005]; int n, cx, cy; void pre() { vt[0].push_back(7); vt[1].push_back(6); vt[2].push_back(5); vt[3].push_back(4), vt[3].push_back(7); vt[4].push_back(3); vt[5].push_back(2); vt[6].push_back(7), vt[6].push_back(1); vt[7].push_back(6), vt[7].push_back(3), vt[7].push_back(0); } int cal_1() { // 计算全部的情况 dp[0][7] = 1; for (int i = 1; i <= n; ++i) { for (int j = 0; j < 8; ++j) { if ((j & mp[i]) != j) continue; for (int k = 0; k < (int)vt[j].size(); ++k) { dp[i][j|ocp[i]] = (1LL*dp[i][j|ocp[i]] + dp[i-1][vt[j][k]])%mod; } } } return dp[n][7]; } int cal_2() { // 计算非法的情况 memset(dp, 0, sizeof (dp)); dp[0][7] = 1; for (int i = 1; i <= n; ++i) { if (i+1 == cx) { // 如果是空点的上一行 for (int j = 0; j < 8; ++j) { if ((j & mp[i]) != j) continue; for (int k = 0; k < (int)vt[j].size(); ++k) { if (!(vt[j][k] & (1 << cy-1))) continue; dp[i][j|ocp[i]] = (1LL*dp[i][j|ocp[i]] + dp[i-1][vt[j][k]])%mod; } } } else if (i == cx) { // 如果是空点的所在行 for (int j = 0; j < 8; ++j) { if ((j & mp[i]) != j) continue; for (int k = 0; k < (int)vt[j].size(); ++k) { if (cy==3&&(j&(1<<cy-2))&&(j&(1<<cy-3))&&(vt[j][k]&(1<<cy-2))&&(vt[j][k]&(1<<cy-3))) continue; if (cy==1&&(j&(1<<cy))&&(j&(1<<cy+1))&&(vt[j][k]&(1<<cy))&&(vt[j][k]&(1<<cy+1))) continue; dp[i][j|ocp[i]] = (1LL*dp[i][j|ocp[i]] + dp[i-1][vt[j][k]])%mod; } } } else if (i-2 == cx) { // 如果是空点的下数第二行 for (int j = 0; j < 8; ++j) { if ((j & mp[i]) != j) continue; for (int k = 0; k < (int)vt[j].size(); ++k) { if (!(vt[j][k] & (1 << cy-1))) continue; dp[i][j|ocp[i]] = (1LL*dp[i][j|ocp[i]] + dp[i-1][vt[j][k]])%mod; } } } else { for (int j = 0; j < 8; ++j) { if ((j & mp[i]) != j) continue; for (int k = 0; k < (int)vt[j].size(); ++k) { dp[i][j|ocp[i]] = (1LL*dp[i][j|ocp[i]] + dp[i-1][vt[j][k]])%mod; } } } } return dp[n][7]; } int main() { pre(); scanf("%d", &n); char str[10005]; for (int i = 1; i <= 3; ++i) { scanf("%s", str+1); for (int j = 1; j <= n; ++j) { mp[j] |= (int)(str[j]=='.') << (i-1); ocp[j] |= (int)(str[j]!='.') << (i-1); if (str[j] == 'O') cx = j, cy = i; } } printf("%d\n", (cal_1()-cal_2()+mod)%mod); return 0; }
题意:给定一棵树,初始化1号节点为红色,其他点为蓝色。有两种操作:1.将某个点从蓝色变成红色;2.询问某个节点到最近的红色的点的距离。
分析:对于1操作保留要改变的点,当有询问时在bfs更新。不过觉得这种做法在树退化成一条链时时间复杂度为O(N^2)。
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <vector> #include <queue> using namespace std; typedef unsigned int uint; const int N = 100005; int dis[N]; char vis[N]; int n, m; vector<int>vt; queue<int>q; struct Edge { int v, nxt; }e[N<<1]; int idx, head[N]; void addedge(int a, int b) { e[idx].v = b, e[idx].nxt = head[a]; head[a] = idx++; } void spread() { memset(vis, 0, sizeof (vis)); for (uint i = 0; i < vt.size(); ++i) { dis[vt[i]] = 0; vis[vt[i]] = 1; q.push(vt[i]); } vt.clear(); while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; ~i; i = e[i].nxt) { int v = e[i].v; if (dis[v] > dis[u] + 1) { dis[v] = dis[u] + 1; if (!vis[v]) vis[v] = 1, q.push(v); } } } } int main() { int a, b; idx = 0; memset(head, 0xff, sizeof (head)); memset(dis, 0x3f, sizeof (dis)); scanf("%d %d", &n, &m); for (int i = 1; i < n; ++i) { scanf("%d %d", &a, &b); addedge(a, b), addedge(b, a); } vt.push_back(1); for (int i = 0; i < m; ++i) { scanf("%d %d", &a, &b); if (a == 1) vt.push_back(b); else spread(), printf("%d\n", dis[b]); } return 0; }