ABC291
D - Flip Cards
Solution:考虑DP,定义状态 \(F_{i,0}\) 为第 \(i\) 张卡片正面朝上的方案数, \(F_{i,1}\) 为第 \(i\) 张卡片背面朝上的方案数,每次check是否相同然后转移即可
int f[N][2]; int a[N]; int b[N]; void solve(){ int n; cin >> n; for (int i = 1;i <= n;i ++) { cin >> a[i] >> b[i]; } f[1][0] = 1; f[1][1] = 1; for (int i = 2;i <= n;i ++) { f[i][0] = (f[i - 1][0] * (a[i - 1] != a[i]) + f[i - 1][1] * (b[i - 1] != a[i])) % mod; f[i][1] = (f[i - 1][0] * (a[i - 1] != b[i]) + f[i - 1][1] * (b[i - 1] != b[i])) % mod; } cout << (f[n][0] + f[n][1]) % mod << endl; }
E - Find Permutation
Solution:考虑到题目所说的大小关系,可以联系到是一个有向图的形式,如果 \(a\) 和 \(b\) 有一条有向边,则含义为 \(a\leqslant b\) ,所以我们可以发现,其实题目只是求一个拓扑序,观察第二个样例,我们还可以发现不能同时有多个点在拓扑序的同一时刻入队,所以就构造完了。
vector<int> G[N]; queue<int> qq; int c[N]; int d[N]; int n, m; vector <int > ans; int f[N]; bool topsort(){ int z=0; for (int i=1;i<=n;i++){ if (!d[i])qq.push(i),z++,ans.push_back(i); } while (!qq.empty()){ if(qq.size() != 1) { return false; } int t = qq.front(); qq.pop(); for (int i : G[t]){ d[i]--; if (!d[i]){ qq.push(i); ans.push_back(i); z++; } } } return z==n; } void solve(){ cin >> n >> m; map<PII,bool> mp; for (int i = 1;i <= m;i ++) { int x,y; cin >> x >> y; d[y]++; G[x].push_back(y); } if(topsort()) { cout << "Yes" << endl; int now = 1; vector<int> last(n + 1); for (int i : ans) {last[i] = now++;} for (int i = 1;i <= n;i ++) cout << last[i] << ' '; cout << endl; } else cout << "No" << endl; }
F- Teleporter and Closed off
Solution:观察到 \(m \leqslant 10\) ,且题目要求的是不经过一个点的时候的最短路,所以对于不经过的点,我们只需要枚举其前面最多 \(10\) 个位置,他们是需要"跨过"当前不能经过的点的,所以DP先处理出来两个最短路,一个从 \(1\) 到后面的点的,一个从后面的点到 \(n\) 的最短路,枚举完点就可以很快的算出答案了。
int f[N][2]; void solve(){ int n , m; cin >> n >> m; vector<string> s(n + 1); for (int i = 2;i <= n;i ++) f[i][1] = 1e18; for (int i = 1;i <= n - 1;i ++) f[i][0] = 1e18; for (int i = 1;i <= n;i ++) { cin >> s[i]; s[i] = " " + s[i]; for (int j = 1;j <= m && i + j <= n;j ++) { if(s[i][j] == '1') f[i + j][1] = min(f[i + j][1], f[i][1] + 1); } } for (int i = n;i >= 1;i --) { for (int j = 1;j <= m && i + j <= n;j ++) { if(s[i][j] == '1') { f[i][0] = min(f[i][0],f[i + j][0] + 1); } } } for (int i = 2;i <= n - 1;i ++) { int ans = 1e18; for (int j = max(1ll,i + 1 - m);j <= i - 1;j ++) { for (int k = i + 1 - j;k <= min(m , n);k ++) { if(s[j][k] == '1') { ans = min(ans , f[j][1] + f[j + k][0] + 1); } } } if(ans != 1e18) cout << ans << ' '; else cout << -1 << ' '; } }
G - OR Sum
Solution:暴力的复杂度是 \(O(N^{2})\) ,所以考虑优化。复杂度来自两个地方,一个是枚举循环的情况, \(O(N)\) ,还有是扫描一遍算或的值, \(O(N)\) ,前一个循环不好优化,因为每个数都很小,所以考虑优化后面这一个运算的复杂度。首先我们可以对每位进行分析, \(A|B = A + B - A \& B\) ,这样子拆开式子后,我们就可以发现,前两项会是一个定值,就是所有元素的和,将 \(B\) 倒序之后,后面一项,对于每一位的情况就是 \(\sum_{i = 1}^{n}A_{i + k} \& B_{n - i}\) ,显然是个卷积的形式,二进制最多五位,做五次卷积分别算贡献就做出来了。
void solve(){ int n; cin >> n; int s = 0; for (int i = 1;i <= n;i ++) cin >> A[i], s += A[i]; for (int i = 1;i <= n;i ++) cin >> B[i], s += B[i]; for (int c = 0;c < 5;c ++) { memset(f,0,sizeof(f));memset(g,0,sizeof(g)); for (int i = 0;i < n;i ++) { f[i] = f[i + n] = (A[i + 1] >> c) & 1; g[i] = (B[n - i] >> c) & 1; } poly_mul(f,g,2 * n); for (int i = 0;i < n;i ++) { ans[i] += f[n + i] * (1ll << c); } } int now = -1; for (int i = 0;i < n;i ++) { now = max(now , s - ans[i]); } cout << now << endl; }