Preface
被折纸狠狠地腐乳了,但好在手速够快光速写完前两题成功把Ohara_Rinne这个号也打上橙了
之后就不开其它小号打了,也差不多该尝试去向上挑战了,不然一直呆在舒适圈内也没啥提升的说
A. Did We Get Everything Covered?
直接把序列自动机建出来,不妨设状态\((x,y)\)表示构造了长度为\(x\)的串,匹配到\(s\)中的位置为\(y\)
不难发现状态总数只有\(O(nm)\)级别,转移的时候再枚举下一个位置填什么字符即可,拿个BFS转移一下
#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=1005;
int t,n,k,m,nxt[N][30],bkt[N],pre[30][N],cs[30][N]; char s[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%s",&n,&k,&m,s+1);
for (i=0;i<k;++i) bkt[i]=m+1;
for (i=m+1;i>=0;--i)
{
for (j=0;j<k;++j) nxt[i][j]=bkt[j];
if (1<=i&&i<=m) bkt[s[i]-'a']=i;
}
for (i=0;i<=n;++i) for (j=0;j<=m+1;++j) pre[i][j]=-1;
queue <pi> q; q.push(pi(0,0));
while (!q.empty())
{
auto [len,pos]=q.front(); q.pop();
if (len==n) continue;
for (i=0;i<k;++i)
{
int npos=nxt[pos][i];
if (pre[len+1][npos]==-1)
pre[len+1][npos]=pos,cs[len+1][npos]=i,q.push(pi(len+1,npos));
}
}
if (pre[n][m+1]==-1) { puts("YES"); continue; }
string ans=""; for (int len=n,pos=m+1;len>0;pos=pre[len][pos],--len) ans+=cs[len][pos]+'a';
reverse(ans.begin(),ans.end()); cout<<"NO"<<endl<<ans<<endl;
}
return 0;
}
B. Space Harbour
很套路的一个题,天天模拟赛写DS终于在CF上派上一点用场了
考虑答案的计算形式,我们要维护的是区间内\(val_{pre}\times dis_{nxt}\)之和,其中\(pre,nxt\)分布表示相对于该点的空间站的前驱(不包括本身)以及后继(包括本身)
稍微手玩一下会发现假设我们要在\(x\)处新建一个空间站,而在建立之前\(x\)的前驱和后继分别为\(l,r\),则:
- 区间\([x,r-1]\)中的所有点对应的\(val\)系数都要变成\(val_x\),但赋值不好处理我们可以转化为区间加来做
- 区间\([l+1,x]\)中的所有点对应的\(dis\)系数都要减去\(r-x\)
因此修改操作其实就是两种系数的区间加,那么我们要快速维护修改后的答案的话,只需要额外维护下区间内两种系数的和即可
找前驱后继可以用set
完成,总复杂度\(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 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=300005;
int n,m,q,a[N],b[N],v[N],tp,x,y; set <int> s;
inline int getpre(CI x)
{
if (x==1) return 0; return *(--s.lower_bound(x));
}
inline int getnxt(CI x)
{
return *s.lower_bound(x);
}
class Segment_Tree
{
private:
struct segment
{
int len,res,sum_val,sum_dis,tag_val,tag_dis;
}O[N<<2];
inline void pushup(CI now)
{
O[now].res=O[now<<1].res+O[now<<1|1].res;
O[now].sum_val=O[now<<1].sum_val+O[now<<1|1].sum_val;
O[now].sum_dis=O[now<<1].sum_dis+O[now<<1|1].sum_dis;
}
inline void apply_val(CI now,CI mv)
{
O[now].res+=O[now].sum_dis*mv;
O[now].sum_val+=O[now].len*mv;
O[now].tag_val+=mv;
}
inline void apply_dis(CI now,CI mv)
{
O[now].res+=O[now].sum_val*mv;
O[now].sum_dis+=O[now].len*mv;
O[now].tag_dis+=mv;
}
inline void pushdown(CI now)
{
if (O[now].tag_val) apply_val(now<<1,O[now].tag_val),apply_val(now<<1|1,O[now].tag_val),O[now].tag_val=0;
if (O[now].tag_dis) apply_dis(now<<1,O[now].tag_dis),apply_dis(now<<1|1,O[now].tag_dis),O[now].tag_dis=0;
}
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 build(TN)
{
O[now].len=r-l+1;
if (l==r)
{
O[now].sum_val=v[getpre(l)];
O[now].sum_dis=getnxt(l)-l;
O[now].res=O[now].sum_val*O[now].sum_dis;
return;
}
int mid=l+r>>1; build(LS); build(RS); pushup(now);
}
inline void modify_val(CI beg,CI end,CI mv,TN)
{
if (beg<=l&&r<=end) return apply_val(now,mv); int mid=l+r>>1; pushdown(now);
if (beg<=mid) modify_val(beg,end,mv,LS); if (end>mid) modify_val(beg,end,mv,RS); pushup(now);
}
inline void modify_dis(CI beg,CI end,CI mv,TN)
{
if (beg<=l&&r<=end) return apply_dis(now,mv); int mid=l+r>>1; pushdown(now);
if (beg<=mid) modify_dis(beg,end,mv,LS); if (end>mid) modify_dis(beg,end,mv,RS); pushup(now);
}
inline int query(CI beg,CI end,TN)
{
if (beg<=l&&r<=end) return O[now].res; int mid=l+r>>1,ret=0; pushdown(now);
if (beg<=mid) ret+=query(beg,end,LS); if (end>mid) ret+=query(beg,end,RS); return ret;
}
#undef TN
#undef LS
#undef RS
}SEG;
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%lld%lld%lld",&n,&m,&q),i=1;i<=m;++i) scanf("%lld",&a[i]);
for (i=1;i<=m;++i) scanf("%lld",&b[i]),v[a[i]]=b[i];
for (i=1;i<=m;++i) s.insert(a[i]);
for (SEG.build(),i=1;i<=q;++i)
{
scanf("%lld%lld%lld",&tp,&x,&y);
if (tp==1)
{
int l=getpre(x),r=getnxt(x); v[x]=y; s.insert(x);
if (x<=r-1) SEG.modify_val(x,r-1,y-v[l]);
if (l+1<=x) SEG.modify_dis(l+1,x,x-r);
} else printf("%lld\n",SEG.query(x,y));
}
return 0;
}
C. Fractal Origami
唉只能说这种题对于我这种脑子不好的人来说就是绝杀,要是组队训练的时候遇到我绝对看一眼就丢给队友的说
这题主要要发现两个trick点,首先是除了第一次折叠外,后面的每一次折叠的都是偶数层,因此每次新产生的山峰和山谷的长度是相等的
然后每次新产生的折痕长度相较于上次,边长是原来的\(\frac{1}{\sqrt 2}\),但数量翻倍了,因此折痕的总长度是上一步的\(\sqrt 2\)倍
不妨设\(S=V+M=\sum_{i=1}^ n 2\sqrt 2\times (\sqrt 2)^{i-1},D=V-M=2\sqrt 2\),则\(\frac{M}{V}=\frac{S-D}{S+D}\)
根据\(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 mod=999999893;
int t,n;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
int main()
{
for (scanf("%d",&t);t;--t)
{
scanf("%d",&n); int A,B,C,D;
if (n%2==0)
{
int x=(quick_pow(2,n/2)-1+mod)%mod;
A=C=4LL*x%mod; B=(2LL*x-2+mod)%mod; D=(2LL*x+2)%mod;
} else
{
int x=4LL*quick_pow(2,(n-1)/2)%mod;
A=C=(x-4+mod)%mod; B=(x-4+mod)%mod; D=x;
}
printf("%d\n",1LL*(1LL*B*C%mod-1LL*A*D%mod+mod)*quick_pow((1LL*C*C%mod-2LL*D*D%mod+mod)%mod)%mod);
}
return 0;
}
D. Balanced Subsequences
最神奇的一题,感觉做法不难而且很典,为什么就是想不到呢
首先考虑容斥,设\(f(n,m,k)\)表示用\(n\)个(
和\(m\)个)
组成的最长合法括号子序列长度不超过\(2k\)的方案数,那么最后的答案就是\(f(n,m,k)-f(n,m,k-1)\)
当\(k\ge \min(n,m)\)时,此时不管怎么放都是合法的,因此\(f(n,m,k)=C_{n+m}^n\)
否则\(f(n,m,k)=f(n-1,m,k-1)+f(n,m-1,k)\),前者代表第一个位置放(
,后者表示第一个位置放)
注意因为此时\(k<\min(n,m)\),因此第一个位置放(
后,后面不管怎么放一定会多出至少一个)
,因此总能匹配上
然后根据数学归纳法以及一些边界条件,可以直接推出\(f(n,m,k)=C_{n+m}^k\)
#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=4005,mod=1e9+7;
int t,n,m,k,fact[N],ifac[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
if (n<0||m<0||n<m) return 0;
return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod;
}
inline int f(CI n,CI m,CI k)
{
if (k>=min(n,m)) return C(n+m,n); else return C(n+m,k);
}
int main()
{
for (scanf("%d",&t),init(4000);t;--t)
scanf("%d%d%d",&n,&m,&k),printf("%d\n",(f(n,m,k)-f(n,m,k-1)+mod)%mod);
return 0;
}
Postscript
接下来好像很长一段时间没有CF了,算是终于能迎来一段小憩的时间了
不存在的还有之前的一些CF和ATC没补,有空还是多写写题吧
标签:typedef,int,Codeforces,long,define,921,Div,include,mod From: https://www.cnblogs.com/cjjsb/p/18000003