Codeforces Round 955 (Div. 2, with prizes from NEAR!)
Preface
由于考试是真彻底结束了,但队友都还没考完,这两天没事只能VP下之前没打的比赛
这场总体感觉题好水,如果不是E想复杂了导致写了1h然后卡常卡了挺久,F看一眼就会了15min就能写完,感觉有机会2h内AK的
A. Soccer
签到,贪心让原来分数高的队先得分是最优的
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,a,b,c,d;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
if (a>b) swap(a,b),swap(c,d);
if (c>d) puts("NO"); else puts("YES");
}
return 0;
}
B. Collatz Conjecture
我们可以先重复进行这样一个暴力过程,每次找到 \(x\) 下一个能被 \(y\) 整除的数,然后变换过去
不发现这个过程在重复 \(\sqrt x\) 级别后 \(x\) 就会变成 \(1\),剩下的部分是个 \(1\to k\to 1\) 的循环,可以直接求解
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,x,y,k;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
scanf("%d%d%d",&x,&y,&k);
while (x!=1&&k)
{
int tar=(x/y+1)*y;
if (k<tar-x) x+=k,k=0;
else
{
k-=tar-x; x=tar;
while (x%y==0) x/=y;
}
}
if (!k) { printf("%d\n",x); continue; }
printf("%d\n",1+k%(y-1));
}
return 0;
}
C. Boring Day
拿个队列维护下当前极长的且未被选择的段,考虑当选中的值之和 \(\ge l\) 时直接贪心地划分掉一定是最优的
同时当出现选中的值之和 \(>r\) 的情形时从队首弹出元素即可,总复杂度 \(O(n)\)
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,l,r,a[N],q[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%d%d",&n,&l,&r),i=1;i<=n;++i) scanf("%d",&a[i]);
RI H=1,T=0; int sum=0,ans=0;
for (i=1;i<=n;++i)
{
q[++T]=a[i]; sum+=a[i];
while (sum>r) sum-=q[H++];
if (sum>=l) ++ans,sum=0,H=1,T=0;
}
printf("%d\n",ans);
}
return 0;
}
D. Beauty of the mountains
很显然的一个题,不妨令 \(diff\) 为初始时两种贡献的差值的绝对值
对于每个 \(k\times k\) 的子矩阵,可以用二维前缀和求出它内部两种颜色的差值 \(c_i\),不难发现每个子矩阵造成的贡献一定是 \(c_i\) 的倍数
由裴蜀定理,设 \(g=\gcd(c_i)\),则有解的充要条件为 \(g\mid diff\),模拟上述过程即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=505;
int t,n,m,k,a[N][N],s[N][N]; char c[N][N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; scanf("%d%d%d",&n,&m,&k); LL diff=0;
for (i=1;i<=n;++i) for (j=1;j<=m;++j) scanf("%d",&a[i][j]);
for (i=1;i<=n;++i) scanf("%s",c[i]+1);
for (i=1;i<=n;++i) for (j=1;j<=m;++j)
{
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]=='1'?1:-1);
if (c[i][j]=='1') diff+=a[i][j]; else diff-=a[i][j];
}
if (diff==0) { puts("YES"); continue; }
diff=abs(diff); int g=0;
for (i=1;i+k-1<=n;++i) for (j=1;j+k-1<=m;++j)
{
int tmp=s[i+k-1][j+k-1]-s[i-1][j+k-1]-s[i+k-1][j-1]+s[i-1][j-1];
tmp=abs(tmp); g=__gcd(g,tmp);
}
if (g==0) puts("NO"); else puts(diff%g?"NO":"YES");
}
return 0;
}
E. Number of k-good subarrays
感觉我又把简单题做复杂了,没去写数位DP去写了个很神秘的东西,最后常数还贼大卡了半天,只能说不愧是我
首先不难想到倍增,假设之前求出了 \([0,2^m-1]\) 中,贡献为 \(0,1,2,\dots,k\) 的答案
则后面一段 \([2^m,2^{m+1}-1]\) 内部的 贡献为 \(0,1,2,\dots,k\) 的答案后可以直接拿来用,剩下的就是考虑左端点在 \([0,2^m-1]\) 中,右端点在 \([2^m,2^{m+1}-1]\) 中的贡献了
不妨直接枚举计算贡献 \(\le i\) 的方案数,最后差分计算具体的方案数即可
这个问题等价于在某个区间 \([l,r]\) 中找到最小/最大的 \(x\) 满足 \(bit(x)>i\),利用一些二进制的性质我们可以 \(O(k)\) 直接求解
最小的就是把 \(l\) 的后缀依次改成全 \(1\);最大的就有些麻烦,需要找到 \(r\) 中所有 \(0\) 出现的位置然后把低位填成 \(1\);这部分的具体实现可以看代码
然后直接做复杂度是单组数据 \(O(k^3)\) 的,但我们可以把倍增过程预处理一下得到一个 \(O(k^3+t\times k^2)\) 的做法,卡卡常数可以通过
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int mod=1e9+7;
int t,k; LL n; array <int,61> f[65]; map <LL,array <int,61>> rst;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
if ((x-=y)<0) x+=mod;
}
inline int find_lst(const LL& l,const LL& r,CI k)
{
LL x=r; int cnt=__builtin_popcountll(x);
if (cnt>k) return 0;
for (RI i=0;i<60;++i)
if ((x>>i)&1)
{
--cnt; x^=(1LL<<i);
if (cnt+i>k)
{
x|=((1LL<<i)-1);
if (x<l) return (r-l+1)%mod;
else return (r-x)%mod;
}
}
return (r-l+1)%mod;
}
inline int find_fst(const LL& l,const LL& r,CI k)
{
LL x=l; int cnt=__builtin_popcountll(x);
if (cnt>k) return 0;
for (RI i=0;i<60;++i)
{
if ((x>>i)&1) --cnt,x^=(1LL<<i);
if (cnt+i+1>k)
{
x|=((1LL<<i+1)-1);
if (x>r) return (r-l+1)%mod;
else return (x-l)%mod;
}
}
return (r-l+1)%mod;
}
inline void init(void)
{
array <int,61> v={0}; v[0]=1; f[0]=v;
LL x=0; for (RI i,j=1;j<61;x=x*2+1,++j)
{
array <int,61> u={0};
for (u[0]=0,i=1;i<61;++i) u[i]=v[i-1];
for (i=1;i<61;++i) inc(v[i],u[i]);
for (i=1;i<61;++i) u[i]=1LL*find_lst(0,x,i)*find_fst(x+1,x*2+1,i)%mod;
for (i=60;i>=1;--i) dec(u[i],u[i-1]);
for (i=1;i<61;++i) inc(v[i],u[i]);
f[j]=v;
}
}
inline array <int,61> solve(const LL& n)
{
if (rst.count(n)) return rst[n];
int k=__lg(n+1); LL x=(1LL<<k)-1;
if (n==x) return rst[n]=f[k]; else
{
RI i; array <int,61> v=f[k],tmp=solve(n-x-1),u={0};
for (u[0]=0,i=1;i<61;++i) u[i]=tmp[i-1];
for (i=1;i<61;++i) inc(v[i],u[i]);
for (i=1;i<61;++i) u[i]=1LL*find_lst(0,x,i)%mod*find_fst(x+1,n,i)%mod%mod;
for (i=60;i>=1;--i) dec(u[i],u[i-1]);
for (i=1;i<61;++i) inc(v[i],u[i]);
return rst[n]=v;
}
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t),init();t;--t)
{
scanf("%lld%d",&n,&k);
array <int,61> tmp=solve(n-1); int ans=0;
for (RI i=0;i<=k;++i) inc(ans,tmp[i]);
printf("%d\n",ans);
}
return 0;
}
F. Sorting Problem Again
很丁真的一个题,我们可以大力维护出数组中所有 \(a_i>a_{i+1}\) 的位置 \(i\) 的集合 \(S\),显然如果不存在这样的位置就说明数组有序
否则令 \(l=\min(S),r=\max(S)+1\),显然要操作的区间至少得包含 \([l,r]\),不妨令 \(mn=\min_\limits{l\le i\le r} a_i,mx=\max_\limits{l\le i\le r} a_i\)
由于分界点的性质,\([1,l-1],[r+1,n]\) 都是单调不降的连续段,因此最后要保留的前缀/后缀就很好计算了,直接在对应的部分里二分一下即可
单点修改和区间最值查询用线段树维护,总复杂度 \(O(n\log n)\)
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=500005,INF=1e9;
int t,n,a[N],q,x,y; set <int> s;
class Segment_Tree
{
private:
int mn[N<<2],mx[N<<2];
public:
#define TN CI now=1,CI l=1,CI r=n
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
inline void updata(CI pos,CI mv,TN)
{
if (l==r) return (void)(mn[now]=mx[now]=mv); int mid=l+r>>1;
if (pos<=mid) updata(pos,mv,LS); else updata(pos,mv,RS);
mn[now]=min(mn[now<<1],mn[now<<1|1]);
mx[now]=max(mx[now<<1],mx[now<<1|1]);
}
inline int query_min(CI beg,CI end,TN)
{
if (beg<=l&&r<=end) return mn[now]; int mid=l+r>>1,ret=INF;
if (beg<=mid) ret=min(ret,query_min(beg,end,LS));
if (end>mid) ret=min(ret,query_min(beg,end,RS));
return ret;
}
inline int query_max(CI beg,CI end,TN)
{
if (beg<=l&&r<=end) return mx[now]; int mid=l+r>>1,ret=-INF;
if (beg<=mid) ret=max(ret,query_max(beg,end,LS));
if (end>mid) ret=max(ret,query_max(beg,end,RS));
return ret;
}
#undef TN
#undef LS
#undef RS
}SEG;
inline void solve(void)
{
if (s.empty()) return (void)(puts("-1 -1"));
int l=*s.begin(),r=*s.rbegin()+1;
int mn=SEG.query_min(l,r),mx=SEG.query_max(l,r);
if (mn<a[1]) l=1; else l=upper_bound(a+1,a+l,mn)-a;
if (mx>a[n]) r=n; else r=lower_bound(a+r+1,a+n+1,mx)-a-1;
printf("%d %d\n",l,r);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i)
scanf("%d",&a[i]),SEG.updata(i,a[i]);
for (s.clear(),i=1;i<n;++i) if (a[i]>a[i+1]) s.insert(i);
for (solve(),scanf("%d",&q),i=1;i<=q;++i)
{
scanf("%d%d",&x,&y);
if (x+1<=n&&a[x]>a[x+1]) s.erase(x);
if (x-1>=1&&a[x-1]>a[x]) s.erase(x-1);
a[x]=y; SEG.updata(x,a[x]);
if (x+1<=n&&a[x]>a[x+1]) s.insert(x);
if (x-1>=1&&a[x-1]>a[x]) s.insert(x-1);
solve();
}
}
return 0;
}
Postscript
继之前给祁神的几何专题胡了几个题面后,徐神也找我外包字符串的题面了,感觉能编题题面的Gal库存要不足了的说