ARC 179 & Codeforces Round 955 (Div. 2, with prizes from NEAR!)
ARC
A
这道题我还不会证明哎。
可以猜想,序列要么是正着排要么是倒着排的。如果都不可以的就输出 No
。具体来说,\(k>0\) 正着,\(k<0\) 倒着。
证明,待补。
B
看到 \(m\le 10\) 就想到一定是 bitmask dp 啦!设 \(dp_{i,msk}\) 为目前填了 \(i\),下一步可以填 \(msk\) 里面的数的方案数。枚举填什么就可以,\(msk\) 的维护就是如果在中间出现了自己对应的那个数就把这一位变成 \(1\)。
时间复杂度 \(\mathcal{O}(n2^mm)\)。
C
先说一个错误的做法。
很容易想到,第一次一定要先排一个序,否则比较难搞。排完序我们首尾配对一直到中间,配成 \(\frac{len}{2}\) 个数(原先 \(len\) 个数),再排序,重复这个过程。
为什么是错的呢?考虑一个例子:\(n=4,r=10,a=\{-10,5,6,9\}\)。这样 \(|5+6|>r=10\),所以不行。
但是我们发现如果只是首尾配对,不配对其他的,一定是可行的。因为:如果都是正数,很显然;如果一个负一个正,加起来一定不超过绝对值的最大值。我们配对以后直接二分插入的位置就可以了。
关于排序有一个 stl 的技巧:stable_sort(id+1,id+1+n,cmp);
,其中 cmp
函数中询问。
CF
A
显然,\(x_1,y_1\) 和 \(x_2,y_2\) 的大小关系不能变才能是 Yes
。
B
我写的是一个玄学复杂度的!就是,我们可以在 \(x\ge y\) 的时候已知暴力(其实加的操作可以直接通过余数算出),等到到了 \(x<y\),除了第一次以外,后面就是循环,可以容易求出。
时间复杂度可以感性理解为 \(\mathcal{O}(\log_y x)\)。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
ll x,y,k,f=0;
cin>>x>>y>>k;
while (k>0 && x>=y){
ll m=x/y;
m++;
m*=y;
ll t=m-x;
if (t>k){
cout<<x+k<<"\n";
f=1;
break;
}
else{
x+=t;
k-=t;
while (x%y==0){
x/=y;
}
}
}
if (f){
continue;
}
if (k==0){
cout<<x<<"\n";
continue;
}
ll t=y-x;
if (t>k){
cout<<x+k<<"\n";
}
else{
k-=t,x=1;
ll m=y-1;
ll r=k%m;
cout<<x+r<<"\n";
}
}
return 0;
}
C
很显然的 dp。设 \(dp_i\) 为考虑到 \(i\) 的最大答案,其中一个转移是 \(dp_i=dp_{i-1}\)。如果用,我们只需要求出可以转移的左右区间即可,这个用二分。区间是单调往右走的,用一个 two pointers 和 set 维护即可。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5+5;
ll n,L,R,a[N],s[N],dp[N];
ll fl(int p){
int l=-1,r=p;
while (l+1<r){
int mid=l+r>>1;
if (s[p]-s[mid]<=R){
r=mid;
}
else{
l=mid;
}
}
return r;
}
ll fr(int p){
int l=-1,r=p;
while (l+1<r){
int mid=l+r>>1;
if (s[p]-s[mid]>=L){
l=mid;
}
else{
r=mid;
}
}
return l;
}
void solve(){
cin>>n>>L>>R;
for (int i=1; i<=n; i++){
dp[i]=0;
cin>>a[i];
s[i]=s[i-1]+a[i];
}
int pl=-1,pr=-1;
dp[0]=0;
multiset<ll> st;
for (int i=1; i<=n; i++){
int lb=fl(i),rb=fr(i);
if (lb<=rb){
while (pr<rb){
pr++;
st.insert(-dp[pr]);
}
while (pl+1<lb){
pl++;
st.erase(st.find(-dp[pl]));
}
}
if (st.size()){
dp[i]=-(*st.begin())+1;
}
dp[i]=max(dp[i],dp[i-1]);
}
cout<<dp[n]<<"\n";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
solve();
}
return 0;
}
D
我们可以用二维前缀和求出每一个 \(k\times k\) 的矩形我们令 \(c=1\) 的贡献是什么。这样问题就变为有若干个数,可以选无数次,能不能和等于另一个数。这个用裴蜀定理即可,具体的,能组成的数一定是那若干个数的 \(\gcd\)。
时间复杂度 \(\mathcal{O}(n^2\log)\)。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e2+2;
ll n,m,k,a[N][N],c[N][N];
string s[N];
ll cal(int x1,int y1,int x2,int y2){
return c[x2][y2]-c[x1-1][y2]-c[x2][y1-1]+c[x1-1][y1-1];
}
ll cal1(int x1,int y1,int x2,int y2){
return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
}
void solve(){
cin>>n>>m>>k;
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
cin>>a[i][j];
}
}
for (int i=1; i<=n; i++){
cin>>s[i];
s[i]=" "+s[i];
for (int j=1; j<=m; j++){
if (s[i][j]=='0'){
c[i][j]=-1;
a[i][j]*=-1;
}
else{
c[i][j]=1;
}
}
}
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
c[i][j]+=c[i-1][j]+c[i][j-1]-c[i-1][j-1];
a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
}
ll g=0;
for (int i=1; i+k-1<=n; i++){
for (int j=1; j+k-1<=m; j++){
ll sum=cal(i,j,i+k-1,j+k-1);
sum=abs(sum);
g=__gcd(g,sum);
}
}
ll sum=cal1(1,1,n,m);
if ((g==0 && sum==0) || (g!=0 && sum%g==0)){
cout<<"YES\n";
}
else{
cout<<"NO\n";
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
solve();
}
return 0;
}
E
有妙妙解法。
设 \(sol(n,k)\) 为答案。(注意,是 \(0\sim n-1\) 的!)那么,我们 \(sol(n,k)\) 可以通过 \(sol(mx,k)\) 和 \(sol(n-mx,k-1)\) 算出来,其中 \(mx\) 是小于 \(n\) 的最大 \(2\) 次幂。(很好理解,大于等于 \(mx\) 的就会有一位固然是 \(1\),就会是 \(k-1\)。)设 \(mx=2^c\)。
问题是怎么算。除了内部的,还有一个端点在左边一个端点在右边的。官方题解是维护了三个值,很复杂。有没有直接算的方法?
我们发现,如果 \(c>k\),那么左边最后一个就会有大于 \(k\) 个 \(1\),没有贡献,所以必须 \(c\le k\)。这个时候左边所有的都满足,只需要计算右边的。右边的到了 \(2^k-1\) 也不行,因为这样也超过了(当然要和长度取最小值)。所以我们算出了右边的贡献 \(s=\min(2^k-1,n-2^c)\)。那么,答案就要多加上 \(s\cdot 2^c\)。
直接记忆化搜索即可,复杂度是 \(\mathcal{O}(k \log n)\) 的。代码非常短。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 1e9+7;
map<pair<ll,ll>,ll> mp;
ll sol(ll n,ll k){
if (k==0 || n==1){
return 1;
}
if (mp.count({n,k})){
return mp[{n,k}];
}
ll c=0;
while ((1ll<<c+1)<n){
c++;
}
ll l=sol(1ll<<c,k),r=sol(n-(1ll<<c),k-1);
ll ans=l+r;
if (c<=k){
ll s=min((1ll<<k)-1,n-(1ll<<c));
ans+=s%mod*((1ll<<c)%mod)%mod;
}
mp[{n,k}]=ans%mod;
return ans%mod;
}
void solve(){
ll n,k;
cin>>n>>k;
cout<<sol(n,k)<<"\n";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
solve();
}
return 0;
}