Codeforces Round 919 (Div. 2)
Preface
妈的我真是智障啊,E题秒出做法没看出复杂度是调和级数的,硬是写不出来
感觉最近唐氏病毒症状又加剧了
A. Satisfying Constraints
签到
#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int INF=1e9;
int t,n,tp,x;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; int L=-INF,R=INF; vector <int> ban;
for (scanf("%d",&n),i=1;i<=n;++i)
{
scanf("%d%d",&tp,&x);
if (tp==1) L=max(L,x);
if (tp==2) R=min(R,x);
if (tp==3) ban.push_back(x);
}
if (L>R) { puts("0"); continue; }
int ans=R-L+1; for (auto x:ban) if (L<=x&&x<=R) --ans;
printf("%d\n",ans);
}
return 0;
}
B. Summation Game
不难发现先手的策略就是删最大的数,后手的策略就是取反最大的数
枚举先手删多少个数然后算一下即可
#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005,INF=1e9;
int t,n,k,x,a[N],pfx[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,&k,&x),i=1;i<=n;++i) scanf("%d",&a[i]);
for (sort(a+1,a+n+1,greater <int>()),i=1;i<=n;++i) pfx[i]=pfx[i-1]+a[i];
int ans=-INF; for (i=0;i<=k;++i)
{
int tmp=min(i+x,n);
auto sum=[&](CI l,CI r)
{
if (l>r) return 0; return pfx[r]-pfx[l-1];
};
ans=max(ans,-sum(i+1,tmp)+sum(tmp+1,n));
}
printf("%d\n",ans);
}
return 0;
}
C. Partitioning the Array
首先数据范围允许大力枚举\(k\),考虑如何检验
对于两个数\(x,y\),满足\(x\bmod m=y\bmod m\)的\(m\)一定是\(|x-y|\)的约数,这个手玩一下很容易看出,然后直接爆枚一下即可
#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005,INF=1e9;
int t,n,a[N],b[N];
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]);
auto solve=[&](CI k)
{
if (k==n) return 1;
RI i,j; for (i=1;i<=n;++i) b[i]=a[i]; int ret=0;
for (i=1;i<=k;++i)
{
int tmp=0;
for (j=i+k;j<=n;j+=k) tmp=__gcd(tmp,abs(b[i]-b[j]));
if (tmp==1) return 0; ret=__gcd(ret,tmp);
}
return ret!=1?1:0;
};
int ans=0; for (i=1;i*i<=n;++i) if (n%i==0)
if (ans+=solve(i),i!=n/i) ans+=solve(n/i);
printf("%d\n",ans);
}
return 0;
}
D. Array Repetition
这题其实就有点唐氏病毒雏形了,秒出的做法结果没分析出复杂度是对的,又想了好久才回头发现原来做法的正确性
首先我们求出所有添加字符时序列的长度,并将这个数组记为关键数组\(\{len_i\}\),如样例一的关键数组就是\(1,2,5\)
然后考虑对于某个询问\(k_i\),我们在关键数组中找到小于等于\(k_i\)且最靠后的元素\(len_j\)
不难发现若\(k_i=len_j\)那么答案就是第\(j\)次添加的字符,否则我们可以令\(k_i=(k_i-1)\bmod len_j+1\),然后递归处理
容易发现每次\(k_i\)的值至少是原来的一半,因此复杂度为\(O(q\log k_i)\)
#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 int long long
#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005,INF=1e18;
int t,n,q,b,x,cnt,len[N],v[N];
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%lld",&t);t;--t)
{
RI i; int cur=0; bool flag=0;
for (scanf("%lld%lld",&n,&q),cnt=0,i=1;i<=n;++i)
{
scanf("%lld%lld",&b,&x);
if (flag) continue;
if (b==1) ++cnt,len[cnt]=++cur,v[cnt]=x; else
{
if (i128(cur)*(x+1)>INF) flag=1; else cur*=(x+1);
}
}
for (i=1;i<=q;++i)
{
scanf("%lld",&x);
for (;;)
{
int p=upper_bound(len+1,len+cnt+1,x)-len-1;
if (len[p]==x) { printf("%lld%c",v[p]," \n"[i==q]); break; }
x=(x-1)%len[p]+1;
}
}
}
return 0;
}
E. Counting Binary Strings
很trivial的DP题,但我就是脑抽了没看出暴力DP的复杂度就是对的
首先分析答案的形式,最后的串一定形如\(0001001001101\),我们求出每个\(1\)左右两边的\(0\)的个数,不难发现good substrings的数量以及限制均和这些值有关
稍作抽象后我们把题目转化成了这样一个问题,求满足以下条件的正整数序列\(\{a_m\}\)的个数:
- \(\sum_{i=1}^{m-1} a_i\times a_{i+1}=n\)
- \(\forall i\in[1,m-1],a_i+a_{i+1}-1\le k\)
一个naive的想法就是直接DP,\(f_{i,j}\)表示当前序列的相邻两项的积的和为\(i\),上一个选的数是\(j\)的方案数
转移的话就枚举下一个数\(t\)选什么,一个显然的观察就是\(t\le \frac{n}{j}\),因此这一维其实是调和级数的
总复杂度\(O(nk\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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2505,mod=998244353;
int t,n,m,f[N][N];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j,k; scanf("%d%d",&n,&m);
for (i=0;i<=n;++i) for (j=0;j<=n;++j) f[i][j]=0;
for (i=1;i<=n;++i) f[0][i]=1;
for (i=0;i<n;++i) for (j=1;j<=n;++j) if (f[i][j])
for (k=1;k<=n&&i+j*k<=n&&j+k-1<=m;++k) inc(f[i+j*k][k],f[i][j]);
int ans=0; for (i=1;i<=n;++i) inc(ans,f[n][i]);
printf("%d\n",ans);
}
return 0;
}
F1. Smooth Sailing (Easy Version)
唉本来这场的上限就是看完E秒了然后把F1写了,这下好了啥都没了
这题一眼二分答案,考虑在check时把不合法的海上格子以及岛屿都打上禁止标记
这需要我们预处理出到每个格子的最近的火山的曼哈顿距离,这个很简单直接以火山为起点跑一个多源BFS即可
然后从起点开始,按照四联通的规则搜索一遍,将能访问的所有点都打上路径标记
现在的问题就是怎么验证选出的路径能完全包围岛屿了,手玩一下会发现这等价于从任意一个岛屿格子出发,经过八连通的规则扩展,在不经过路径标记的格子的条件下,无法到达边界
因此再从所有边界格子为起点跑一个多源BFS即可,总复杂度\(O(q\times nm\log nm)\)
#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 __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=300005,dx[8]={0,1,0,-1,1,1,-1,-1},dy[8]={1,0,-1,0,1,-1,1,-1};
int n,m,q; string a[N]; vector <int> dis[N],valid[N],vis[N],rbd[N];
inline void DFS(CI x,CI y)
{
vis[x][y]=1; for (RI i=0;i<4;++i)
{
int nx=x+dx[i],ny=y+dy[i];
if (nx<1||nx>n||ny<1||ny>m) continue;
if (valid[nx][ny]&&!vis[nx][ny]) DFS(nx,ny);
}
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
ios::sync_with_stdio(false); cin.tie(0);
RI i,j; for (cin>>n>>m>>q,i=1;i<=n;++i)
cin>>a[i],a[i]=" "+a[i],dis[i].resize(m+1),valid[i].resize(m+1),vis[i].resize(m+1),rbd[i].resize(m+1);
queue <pi> Q; for (i=1;i<=n;++i) for (j=1;j<=m;++j)
if (a[i][j]=='v') dis[i][j]=0,Q.push(pi(i,j)); else dis[i][j]=-1;
while (!Q.empty())
{
auto [x,y]=Q.front(); Q.pop();
for (i=0;i<4;++i)
{
int nx=x+dx[i],ny=y+dy[i];
if (nx<1||nx>n||ny<1||ny>m) continue;
if (dis[nx][ny]==-1) dis[nx][ny]=dis[x][y]+1,Q.push(pi(nx,ny));
}
}
for (i=1;i<=q;++i)
{
int sx,sy; cin>>sx>>sy; int l=0,r=n+m,mid,ret=-1;
auto check=[&](CI lim)
{
RI i,j; for (i=1;i<=n;++i) for (j=1;j<=m;++j)
{
vis[i][j]=0;
if (a[i][j]=='#') valid[i][j]=0; else valid[i][j]=(dis[i][j]>=lim);
}
if (!valid[sx][sy]) return false;
for (DFS(sx,sy),i=1;i<=n;++i) for (j=1;j<=m;++j)
if ((i==1||i==n||j==1||j==m)&&!vis[i][j]) rbd[i][j]=1,Q.push(pi(i,j)); else rbd[i][j]=0;
while (!Q.empty())
{
auto [x,y]=Q.front(); Q.pop();
for (i=0;i<8;++i)
{
int nx=x+dx[i],ny=y+dy[i];
if (nx<1||nx>n||ny<1||ny>m) continue;
if (!vis[nx][ny]&&!rbd[nx][ny]) rbd[nx][ny]=1,Q.push(pi(nx,ny));
}
}
for (i=1;i<=n;++i) for (j=1;j<=m;++j)
if (a[i][j]=='#'&&rbd[i][j]) return false;
return true;
};
while (l<=r) if (check(mid=l+r>>1)) ret=mid,l=mid+1; else r=mid-1;
printf("%d\n",ret);
}
return 0;
}
F2. Smooth Sailing (Hard Version)
F2好像是用射线法判断在多边形内部来做,然后套个克鲁斯卡尔重构树,有时间去补一下吧
Postscript
这场要是打好点的话小小号估计就2050+了,下场就是上橙渡劫局了
不过有一说一这个号5场打到2000分也还行了,看看能不能下两场之内上个橙

浙公网安备 33010602011771号