比赛链接
Solved: 6/11
Rank: fst,寄!
B 先读错题再 fst,绷不住了。。。
A. Kevin and Arithmetic
题意:给一个序列,初始 \(s=0\)。对 \(i=1,\dots,n\) 依次进行如下操作:\(s\leftarrow s+a_i\),然后若 \(s\) 为偶数则分数 +1 并不断除 2 直到 \(s\) 为奇数。重排序列使得分数最高。
偶数除了第一个以外其他都不会产生贡献,答案就是奇数个数加 1。特判所有数都是奇数的情况。
void solve(){int n;cin>>n;vector<int> a(n);cin>>a;int cnt=0;for(int i=0;i<n;++i)if(a[i]&1)++cnt;cout<<(n==cnt?n-1:cnt+1)<<'\n';
}
B. Kevin and Geometry
题意:给 \(n\) 个数,选出其中 4 个数使得它们可以组成等腰梯形。
设四条边长度为 \(a,b,c,c\),且 \(a<b\),则充要条件是 \(b-a<2c\)。
将所有数排序后枚举相等的数 \(c\),判断剩下的数里是否有差小于 \(2c\) 的两个数即可。这个可以用差分数组前后缀的 min 做。
fst 因:没考虑 \(a<c<b\) 的情况!
void solve(){int n;cin>>n;vector<int> a(n);cin>>a;sort(all(a));vector<int> b(n-1);for(int i=0;i<n-1;++i)b[i]=a[i+1]-a[i];vector<int> p(n-1),s(n-1);for(int i=0;i<n-1;++i){if(i==0)p[i]=b[i];else p[i]=min(p[i-1],b[i]);}for(int i=n-2;i>=0;--i){if(i==n-2)s[i]=b[i];else s[i]=min(s[i+1],b[i]);}for(int i=0;i<=n-2;++i)if(a[i]==a[i+1]){if(i>1&&p[i-2]<a[i]*2){for(int j=0;j<=i-2;++j)if(b[j]==p[i-2]){cout<<a[j]<<' '<<a[j+1]<<' '<<a[i]<<' '<<a[i+1]<<'\n';return;}}if(i<n-3&&s[i+2]<a[i]*2){for(int j=n-2;j>=i+2;--j)if(b[j]==s[i+2]){cout<<a[j]<<' '<<a[j+1]<<' '<<a[i]<<' '<<a[i+1]<<'\n';return;}}if(i>=1&&i<=n-3&&a[i+2]-a[i-1]<a[i]*2){cout<<a[i-1]<<' '<<a[i]<<' '<<a[i+1]<<' '<<a[i+2]<<'\n';return;}}cout<<"-1\n";
}
C. Kevin and Puzzle
题意:\(n\) 个人站成一排,其中有一部分人是说谎者。每个人会给出站在自己左侧的说谎者的数量 \(a_i\),说谎者提供的 \(a_i\) 不可信(不一定错误),相邻两个人不可能都是说谎者。问这 \(n\) 个人的身份的方案数。
dp,\(f(i,0/1)\) 表示 \(i\) 说真话/假话的方案数。如果 \(i\) 说真话,需要 \(a_i=a_{i-1}\)(\(i-1\) 真)或 \(a_i=a_{i-2}+1\)(\(i-1\) 假)。
void solve(){int n;cin>>n;vector<int> a(n+1);for(int i=1;i<=n;++i)cin>>a[i];vector<array<ll,2>> f(n+1);f[0][0]=1;for(int i=1;i<=n;++i){if(a[i]==a[i-1])f[i][0]=(f[i][0]+f[i-1][0])%mod;if(i==1||a[i]==a[i-2]+1)f[i][0]=(f[i][0]+f[i-1][1])%mod;f[i][1]=(f[i][1]+f[i-1][0])%mod;}cout<<(f[n][0]+f[n][1])%mod<<'\n';
}
D. Kevin and Numbers
题意:给两个集合 \(A\) 和 \(B\)。每次可以在 \(A\) 中选择两个差不超过 \(1\) 的数合并为它们的和,问能否变成 \(B\)。
逆向思考。如果 \(B\) 的最大值 \(x\) 在 \(A\) 中不存在,那么它一定是通过 \(\lfloor \frac x2\rfloor\) 和 \(\lceil \frac x2\rceil\) 合成得到的。如果存在,则一定是 \(A\) 中相同的数直接保留。
用 multiset 模拟这个逆向过程即可。
bool solve(){int n,m;cin>>n>>m;multiset<int> a,b;ll s=0;for(int i=0,x;i<n;++i)cin>>x,a.insert(x),s+=x;for(int i=0,x;i<m;++i)cin>>x,b.insert(x),s-=x;if(s)return false;while(a.size()&&b.size()){int x=*a.rbegin(),y=*b.rbegin();if(x>y)return false;if(x==y){a.erase(a.find(x));b.erase(b.find(y));}else{b.erase(b.find(y));b.insert(y/2);b.insert((y+1)/2);}}return a.empty()&&b.empty();
}
E. Kevin and And
题意:有两个集合 \(A\) 和 \(B\)。你可以做 \(k\) 次操作,每次选择 \(a_i\in A, b_j\in B\) 并令 \(a_i\leftarrow a_i\& b_j\)。问操作结束后 \(A\) 所有数之和的最小值。\(n\leq 10^5,m\leq 10\)。
首先可以 \(O(n2^m)\) 求出 \(A\) 中每个数做 \(1\) 到 \(10\) 次操作得到的最小值。对于单个 \(i\),操作的收益是单调减的,因此可以用堆维护这个操作过程。复杂度 \(O(n2^m+k\log n)\)。
const int N=1e5+5,M=10;
int n,m,q,d[N];
ll a[N],b[M],c[1<<M],f[N][M+1];
void solve(){cin>>n>>m>>q;for(int i=1;i<=n;++i)cin>>a[i];for(int i=0;i<m;++i)cin>>b[i];for(int i=0;i<1<<m;++i){c[i]=(1<<30)-1;for(int j=0;j<m;++j)if(i>>j&1)c[i]&=b[j];}for(int i=1;i<=n;++i){for(int j=0;j<=m;++j)f[i][j]=0;for(int j=0;j<1<<m;++j){int k=__builtin_popcount(j);f[i][k]=max(f[i][k],a[i]-(a[i]&c[j]));}}priority_queue<pll> pq;ll ans=0;for(int i=1;i<=n;++i)pq.push({f[i][1],i}),ans+=a[i],d[i]=0;while(q--){int i=pq.top().second;ans-=pq.top().first;pq.pop();++d[i];if(d[i]<m)pq.push({f[i][d[i]+1]-f[i][d[i]],i});}cout<<ans<<'\n';
}
F1. Kevin and Binary String (Easy Version)
题意:给定 01 串 \(s,t\),每次可以交换 \(s\) 相邻的极长 0 连续段和极长 1 连续段,求最少交换次数使 \(s\) 变成 \(t\)。若无法做到则输出 -1
首先在 \(s\) 和 \(t\) 的开头都插入一个 \(t_1\),结尾都插入一个 \(t_n\),答案不变。
插入之后,每次操作都恰好会减少两个极长连续段。因此答案就是极长连续段数之差除 2。我们只需判断能否做到。
设 \(s\) 的极长连续段长为 \(a_1,\dots,a_m\)。如果我们交换 \(s\) 的第 \(i\) 段和第 \(i+1\) 段,实际上就是令 \(a_i\leftarrow a_i+a_{i+2}, a_{i+1}\leftarrow a_{i+1}+a_{i+3}\)。
因此直接在 \(s\) 和 \(t\) 的极长连续段长序列上双指针枚举,如果相等就匹配,如果不相等就尝试交换操作。
如果你 WA4 了,请注意 \(a_i\) 和 \(b_j\) 匹配上了并不代表 \(a_{i+1}\) 也要和 \(b_{j+1}\) 匹配。
const int N=4e5+5;
int n,m1,m2,a[N],b[N];
string s,t;
int solve(){cin>>s>>t;n=s.length();char u=t[0],v=t[n-1];s=u+s+v,t=u+t+v,n+=2;int len=0;m1=m2=0;for(int i=0;i<n;++i){if(i>0&&s[i]!=s[i-1])a[++m1]=len,len=1;else ++len;}a[++m1]=len;len=0;for(int i=0;i<n;++i){if(i>0&&t[i]!=t[i-1])b[++m2]=len,len=1;else ++len;}b[++m2]=len;int i=1,j=1;while(j<=m2){if(i>m1)return -1;if(b[j]<a[i])return -1;if(b[j]==a[i]){++i,++j;continue;}if(i==m1)return -1;int x=a[i],y=a[i+1];if(j+1<=m2){while(i+3<=m1){x+=a[i+2],y+=a[i+3],i+=2;if(x>b[j])return -1;if(x==b[j])break;}if(x==b[j])++i,++j,a[i]=y;else return -1;}else return -1;}if(i>m1)return (m1-m2)/2;else return -1;
}