SMU 2024 ptlks的周报Week 10 (7.22-7.28)
这周学了SOSdp,学会了感觉还挺简单的。核心就几行代码:
//#include <bits/stdc++.h>
for (int j = 0; j < k; j++) {
for (int i = 0; i < (1 << k); i++) {
if ((i & (1 << j))) {
dp[i] += dp[i ^ (1 << j)];
}
}
}
基本思路就是按照子集来dp。
E. Compatible Numbers
题意:给定数组,对每个数找出数组中与它互斥的数(AND为0)。
思路:板子题,按子集dp即可
代码
#include <bits/stdc++.h>
//#define int long long
#define mod 998244353
#define PII pair<int,int>
#define PIII pair<int,PII>
#define double long double
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, SZ = (N) << 2;
int mx = 0;
int n = 1e5, k, m = 1e5;
vector<PII>g[N];
int fa[N];
int a[N];
int xx[4] = {0, 1, 0, -1}, yy[4] = {-1, 0, 1, 0};
int dp[1 << 22];
void solve() {
cin >> n ;
for (int i = 1; i <= n; i++) {
cin >> a[i];
dp[a[i]] = a[i];
}
for (int j = 0; j < 22; j++) {
for (int i = 0; i < (1 << 22); i++) {
// cout<<i<<endl;
if ((i & (1 << j)) && dp[i ^ (1 << j)]) {
dp[i] = dp[i ^ (1 << j)];
}
}
}
for (int i = 1; i <= n; i++) {
int j =( a[i] ^ ((1 << 22) - 1));
// cout<<j<<endl;
// cout<<a[i]<<' '<<dp[j]<<endl;
if (dp[j]) {
cout << dp[j] << ' ';
} else {
cout << -1 << ' ';
}
}
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
E - Or Plus Max
题意:给定长度为\(2^n\)的序列,对于每个\((1\leq k\leq 2^n-1)\)找出最大的\(a_i+a_j(i\,or\,j\leq k,0\leq i<j<2^n)\)并输出。
思路:dp维护k子集内的最大值和次大值。
代码
#include <bits/stdc++.h>
//#define int long long
#define mod 998244353
#define PII pair<int,int>
#define PIII pair<int,PII>
#define double long double
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, SZ = (N) << 2;
int mx = 0;
int n = 1e5, k, m = 1e5;
vector<PII>g[N];
int fa[N];
int a[N];
int xx[4] = {0, 1, 0, -1}, yy[4] = {-1, 0, 1, 0};
int dp[1 << 22][2];
void solve() {
cin >> n ;
for (int i = 0; i <(1<<n); i++) {
cin >> a[i];
dp[i][0]=a[i];
}
for (int j = 0; j < 22; j++) {
for (int i = 0; i < (1 << 22); i++) {
// cout<<i<<endl;
if ((i & (1 << j))) {
if(dp[i][0]<dp[i^(1<<j)][0]){
dp[i][1]=dp[i][0];
dp[i][0]=dp[i^(1<<j)][0];
if(dp[i][1]<dp[i^(1<<j)][1]){
dp[i][1]=dp[i^(1<<j)][1];
}
}else if(dp[i][0]>=dp[i^(1<<j)][0]&&dp[i^(1<<j)][0]>dp[i][1]){
dp[i][1]=dp[i^(1<<j)][0];
}
}
}
}
int mx=0;
for (int i = 1; i <(1<<n); i++) {
mx=max(dp[i][0]+dp[i][1],mx);
cout<<mx<<endl;
}
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
D. Cases
题意:给定字符串,字符串由若干个长度不大于k的单词组成,求单词末尾组成的集合大小的最小值。
思路:设单词末尾组成的集合为T,不难得出,字符串中每k个连续字符至少有一个字符与T相交,设这k个连续字符组成的集合为B,则\(T \cap\complement _U B=\varnothing\),考虑将所有\(\complement _U B\)的子集标记,对所有未标记的T的大小取最小值即可。
代码
#include <bits/stdc++.h>
//#define int long long
#define mod 998244353
#define PII pair<int,int>
#define PIII pair<int,PII>
#define double long double
#define endl '\n'
using namespace std;
const int N = 5e5 + 10, SZ = N << 2;
int mx = 0;
int n = 1e5, k, m = 1e5,c;
vector<PII>g[N];
int fa[N];
int a[N];
int xx[4] = {0, 1, 0, -1}, yy[4] = {-1, 0, 1, 0};
int fac[33];
int qpow(int x,int n){
int ans=1;
while(n){
if(n&1){
ans*=x;
ans%=mod;
}
x*=x;
x%=mod;
n>>=1;
}
return ans;
}
int C(int n,int m){
return fac[n]*qpow(fac[m],mod-2)%mod*qpow(fac[n-m],mod-2);
}
int dp[N];
int cnt[20];
void solve() {
cin >> n>>c >>k;
int s=1;
for(int i=1;i<=n;i++){
char x;
cin>>x;
a[i]=x-'A';
}
for (int j = 0; j < c; j++) {
cnt[j]=0;
for (int i = 0; i < (1 << c); i++) {
dp[i]=0;
}
}
int p=0;
for(int i=1;i<=k;i++){
cnt[a[i]]++;
p|=(1<<a[i]);
}
int msk=(1<<c)-1;
dp[msk^p]=1;
for(int i=k+1;i<=n;i++){
cnt[a[i-k]]--;
if(!cnt[a[i-k]])p^=(1<<a[i-k]);
cnt[a[i]]++;
p|=(1<<a[i]);
dp[msk^p]=1;
}
int mn=c;
for (int j = 0; j < c; j++) {
for (int i = 0; i < (1 << c); i++) {
if ((i & (1 << j))) {
dp[i ^ (1 << j)]|=dp[i];
}
}
}
for (int i = 0; i < (1 << c); i++) {
if(i&(1<<a[n]))
if (!dp[i]) {
mn=min(mn,__builtin_popcount((unsigned)i));
}
}
cout<<mn<<endl;
}
int32_t main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}