IOI2020 集训队作业 Part 2
CF627E Orchestra
CF639E Bear and Paradox
CF639F Bear and Chemistry
CF666D Chain Reaction
CF666E Forensic Examination
CF671D Roads in Yusland
CF671E Organizing a Race
CF643D Bearish Fanpages
CF643F Bears and Juice
CF643G Choosing Ads
#include<bits/stdc++.h>
using namespace std;
const int maxn=150010;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
int x=0,f=0;char ch=getchar();
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,q,p,cov[maxn*4],a[maxn];
struct fuck{
int x[5],cnt[5];
fuck(){MEM(x,0);MEM(cnt,0);}
void insert(int x_,int cnt_){
FOR(i,0,p-1) if(x_==x[i]) return void(cnt[i]+=cnt_);
FOR(i,0,p-1) if(!x[i]) return void((x[i]=x_,cnt[i]=cnt_));
int mn=0;
FOR(i,1,p-1) if(x[i] && cnt[i]<cnt[mn]) mn=i;
if(cnt_<cnt[mn]) FOR(i,0,p-1) cnt[i]-=cnt_;
else{
int tmp=cnt[mn];
x[mn]=x_;cnt[mn]=cnt_;
FOR(i,0,p-1) cnt[i]-=tmp;
}
}
fuck operator+(fuck f)const{
fuck ans=*this;
FOR(i,0,p-1) if(f.x[i]) ans.insert(f.x[i],f.cnt[i]);
return ans;
}
}seg[maxn*4];
inline void cover(int o,int l,int r,int v){
FOR(i,0,p-1) seg[o].x[i]=seg[o].cnt[i]=0;
seg[o].x[0]=v;seg[o].cnt[0]=r-l+1;
cov[o]=v;
}
inline void pushdown(int o,int l,int r){
if(cov[o]){
int mid=(l+r)>>1;
cover(lson,cov[o]);
cover(rson,cov[o]);
cov[o]=0;
}
}
void build(int o,int l,int r){
if(l==r) return void((seg[o].x[0]=a[l],seg[o].cnt[0]=1));
int mid=(l+r)>>1;
build(lson);build(rson);
seg[o]=seg[o<<1]+seg[o<<1|1];
// printf("[%d,%d]:\n",l,r);
// FOR(i,0,p-1) printf("%d %d\n",seg[o].x[i],seg[o].cnt[i]);
}
void update(int o,int l,int r,int ql,int qr,int v){
if(l>=ql && r<=qr) return cover(o,l,r,v);
pushdown(o,l,r);
int mid=(l+r)>>1;
if(mid>=ql) update(lson,ql,qr,v);
if(mid<qr) update(rson,ql,qr,v);
seg[o]=seg[o<<1]+seg[o<<1|1];
}
fuck query(int o,int l,int r,int ql,int qr){
if(l>=ql && r<=qr) return seg[o];
pushdown(o,l,r);
int mid=(l+r)>>1;
if(mid<ql) return query(rson,ql,qr);
if(mid>=qr) return query(lson,ql,qr);
return query(lson,ql,qr)+query(rson,ql,qr);
}
int main(){
n=read();q=read();p=100/read();
FOR(i,1,n) a[i]=read();
build(1,1,n);
while(q--){
int op=read(),x=read(),y=read();
if(op==1) update(1,1,n,x,y,read());
else{
fuck ans=query(1,1,n,x,y);
printf("%d ",p);
FOR(i,0,p-1) printf("%d ",ans.x[i]);
puts("");
}
}
}
CF679E Bear and Bad Powers of 42
CF685C Optimal Point
CF696F ...Dary!
CF698D Limak and Shooting Points
考虑对于每一个怪判断有没有可能被打死。
假如 \(k\) 个射击点的访问顺序是 \(p_1,p_2,\dots,p_k\)。不妨钦定是 \(p_k\) 最后射死这只怪物,\(p_{k-1}\) 用来射挡住它的最后一个怪物,\(p_{k-2}\) 用来……自行体会。
这样肯定枚举到了所有可能有用的方案。
问题就很简单了,对于每个怪物大暴力,然后瞎搞。
时间复杂度看实现,我的实现是 \(O(n^2k+nk^2\times k!)\),完全跑不满,跑得飞快。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxk=11,maxn=1111;
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
int x=0,f=0;char ch=getchar();
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int k,n,sx[maxk],sy[maxk],tx[maxn],ty[maxn],p[maxn],piv,ans,seq[maxk],cur,tmp[maxk],tl;
vector<int> v[maxk][maxn];
bool used[maxk],vis[maxn];
inline ll dis(int x1,int y1,int x2,int y2){
int xd=x1-x2,yd=y1-y2;
return 1ll*xd*xd+1ll*yd*yd;
}
inline bool cmp(int x,int y){
return dis(sx[piv],sy[piv],tx[x],ty[x])>dis(sx[piv],sy[piv],tx[y],ty[y]);
}
inline bool on(int x,int y,int x1,int y1,int x2,int y2){
int xd1=x1-x,yd1=y1-y,xd2=x2-x,yd2=y2-y;
if(1ll*xd1*yd2!=1ll*yd1*xd2) return false;
if(x<min(x1,x2) || x>max(x1,x2)) return false;
if(y<min(y1,y2) || y>max(y1,y2)) return false;
return true;
}
bool check(int to){
int now=--cur;
FOR(i,0,(int)v[seq[now]][to].size()-1){
int x=v[seq[now]][to][i];
if(vis[x]) continue;
if(!check(x) || !cur) return false;
}
if(cur) return vis[tmp[++tl]=to]=true;
else return false;
}
bool dfs(int dep,int to){
if(dep>k){
cur=k+1;tl=0;
bool flag=check(to);
FOR(i,1,tl) vis[tmp[i]]=false;
return flag;
}
FOR(i,1,k) if(!used[i]){
used[i]=true;
seq[dep]=i;
bool flag=dfs(dep+1,to);
used[i]=false;
if(flag) return true;
}
return false;
}
int main(){
k=read();n=read();
FOR(i,1,k) sx[i]=read(),sy[i]=read();
FOR(i,1,n) tx[i]=read(),ty[i]=read();
FOR(i,1,k){
piv=i;
FOR(j,1,n) p[j]=j;
sort(p+1,p+n+1);
FOR(j,1,n) FOR(k,1,n) if(j!=p[k] && on(tx[p[k]],ty[p[k]],sx[i],sy[i],tx[j],ty[j])) v[i][j].push_back(p[k]);
}
FOR(i,1,n) if(dfs(1,i)) ans++;
printf("%d\n",ans);
}
CF700E Cool Slogans
CF704B Ant Man
CF704C Black Widow
CF704D Captain America
CF704E Iron Man
CF708D Incorrect Flow
CF708E Student's Camp
校内模拟赛搬了这题,感觉全场就我没见过……虽然还是硬做出来了(
首先,每一排留下的肯定是一个区间。如果什么都没留下了,那就不合法了(
同时,相邻两排的区间一定有交。
那么先来个五次方暴力:\(f_{i,l,r}\) 表示算了前 \(i\) 排,第 \(i\) 排的区间是 \([l,r]\),且合法的概率。
转移暴力枚举上一个区间是什么。这一排变成这个区间的概率是个组合式子。
然后很容易优化到三次方:反面考虑,上一排所有区间减去没有交的区间。没有交的区间就两种:它的 \(l\) 大于这一排的 \(r\),或者它的 \(r\) 小于这一排的 \(l\)。前缀和/后缀和。
怎么变成平方呢?状态数就三次方了,所以我们不管原来的状态,直接算出前缀和和后缀和。
这里以前缀和为例。我们先算出所有右端点恰好为 \(j\) 的概率之和。大概是个形如 \(\displaystyle c\sum_{i=1}^j(s-x_i)\binom{k}{i-1}p^{i-1}(1-p)^{k-(i-1)}\) 的式子。
后面这个可能可以接着化,但此时再上个前缀和就够了。
由于场上赶时间,写得比较丑。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1515,maxk=222222,mod=1000000007;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,m,p,k,pre[2][maxn],suf[2][maxn],cur,fac[maxk],inv[maxk],invfac[maxk],ans,pw1[maxk],pw2[maxk];
inline int qpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
return ans;
}
inline int C(int n,int m){
if(n<m || n<0 || m<0) return 0;
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
int main(){
n=read();m=read();
p=read();p=1ll*p*qpow(read(),mod-2)%mod;
k=read();
fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1;
FOR(i,2,k){
fac[i]=1ll*fac[i-1]*i%mod;
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
}
pw1[0]=pw2[0]=1;
FOR(i,1,2*k){
pw1[i]=1ll*pw1[i-1]*p%mod;
pw2[i]=1ll*pw2[i-1]*(1-p+mod)%mod;
}
cur=1;
FOR(i,1,m) FOR(j,i,m) if(i-1<=k && m-j<=k){
int x=1ll*C(k,i-1)*C(k,m-j)%mod*pw1[i-1+m-j]%mod*pw2[2*k-(i-1+m-j)]%mod;
pre[0][j]=(pre[0][j]+x)%mod;
suf[0][i]=(suf[0][i]+x)%mod;
}
FOR(i,1,m) pre[0][i]=(pre[0][i-1]+pre[0][i])%mod;
ROF(i,m,1) suf[0][i]=(suf[0][i+1]+suf[0][i])%mod;
FOR(_,2,n){
FOR(i,1,m) pre[cur][i]=suf[cur][i]=0;
int s1=0,s2=0;
FOR(j,1,m){
if(j-1<=k){
s1=(s1+1ll*C(k,j-1)*pw1[j-1]%mod*pw2[k-(j-1)])%mod;
s2=(s2+1ll*C(k,j-1)*pw1[j-1]%mod*pw2[k-(j-1)]%mod*pre[cur^1][j-1])%mod;
}
if(m-j<=k){
int c=1ll*C(k,m-j)%mod*pw1[m-j]%mod*pw2[k-(m-j)]%mod;
int s=(pre[cur^1][m]-suf[cur^1][j+1]+mod)%mod;
pre[cur][j]=(pre[cur][j]+1ll*c*(1ll*s*s1%mod-s2+mod))%mod;
}
}
s1=s2=0;
ROF(i,m,1){
if(m-i<=k){
s1=(s1+1ll*C(k,m-i)*pw1[m-i]%mod*pw2[k-(m-i)])%mod;
s2=(s2+1ll*C(k,m-i)*pw1[m-i]%mod*pw2[k-(m-i)]%mod*suf[cur^1][i+1])%mod;
}
if(i-1<=k){
int c=1ll*C(k,i-1)%mod*pw1[i-1]%mod*pw2[k-(i-1)]%mod;
int s=(pre[cur^1][m]-pre[cur^1][i-1]+mod)%mod;
suf[cur][i]=(suf[cur][i]+1ll*c*(1ll*s*s1%mod-s2+mod))%mod;
}
}
FOR(i,1,m) pre[cur][i]=(pre[cur][i-1]+pre[cur][i])%mod;
ROF(i,m,1) suf[cur][i]=(suf[cur][i+1]+suf[cur][i])%mod;
cur^=1;
}
printf("%d\n",pre[cur^1][m]);
}
[AGC020D] Min Max Repetition
[AGC020E] Encoding Subsets
有坑待填!有坑待填!有坑待填!!!
考虑暴力。(¿¿¿)
\(f_S\) 表示字符串 \(S\) 的所有子集的重写方案之和,\(g_S\) 表示字符串 \(S\) 的所有子集的不可分割的(指不能拆出一些带 \(\texttt{x}\) 的,\(\texttt{0011101}\) 这种视为不可分割)重写方案之和。
边界 \(g_0=1,g_1=2\),答案为 \(f_{\text{原串}}\)。
上面是枚举最后一个可分割的位置,下面是枚举循环节长度。
上记忆化搜索。然后瞎写都行。
时间复杂度证明:
一个串能出现在状态里,是因为它是原串,或者某个可到达的状态的子串,或者某个可到达的状态缩起来的。
- 对于长度 \(\le\frac{n}{8}\) 的串,假如出现在状态中,也至多是 \(O(2^{n/8})\) 种。
- 对于长度 \(>\frac{n}{8}\) 的串,至多被缩两次:
- 对于没被缩的串,也就是原串的子串,只有 \(O(n^2)\) 种。
- 对于只被缩一次的串,可以被表示成 \(S[i\dots i+k-1]\& S[j\dots j+k-1]\),只有 \(O(n^3)\) 种。
- 对于被缩两次的串:(其实,下面这一块我不懂为什么不是 \(O(n^4)\)……)
- 对于选个子串,\(2\) 倍缩,再选个子串,再 \(2\) 倍缩,最后选个子串的,一定可以表示成 \(S[i\dots i+k-1]\& S[i+k\dots i+2k-1]\& S[j\dots j+k-1]\& S[j+k\dots j+2k-1]\) 的形式。所以一共是 \(O(n^3)\) 种。
- 对于 \(2\) 倍缩 \(3\) 倍缩的,类似;
- 对于 \(3\) 倍缩 \(2\) 倍缩的,类似。
所以状态数上界 \(O(n^3+2^{n/8})\),其中 \(n^3\) 前面还带着个小常数。(如果是 \(O(n^4)\) 也带着个超小常数就对了……)
实践证明,状态数至多只有大概 \(5\times 10^4\)。上面给出的上界仍然是比较松的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
string s;
map<string,int> f,g;
int G(string);
int F(string s){
if(f.count(s)) return f[s];
int n=s.size(),ans=G(s);
FOR(i,1,n-1) ans=(ans+1ll*F(s.substr(0,i))*G(s.substr(i,n)))%mod;
return f[s]=ans;
}
int G(string s){
if(g.count(s)) return g[s];
int n=s.size();
if(n==1) return s[0]-'0'+1;
int ans=0;
FOR(i,1,n-1) if(n%i==0){
string t;
FOR(j,0,i-1){
bool flag=true;
for(int k=j;k<n;k+=i) flag&=s[k]-'0';
t+=flag+'0';
}
ans=(ans+F(t))%mod;
}
return g[s]=ans;
}
int main(){
cin>>s;
cout<<F(s)<<endl;
}
[AGC020F] Arcs on a Circle
[AGC021E] Ball Eat Chameleons
[AGC021F] Trinity
[AGC022D] Shopping
[AGC022E] Median Replace
考虑怎么判一个串合法。
从左往右扫,维护一个栈,栈从底到顶是 111...11000..00 的形式,为什么后面会说。
遇到一个 0 的时候:
- 如果栈顶两个 0,那么最优方案是把这连续的三个 0 操作一次变成一个 0,所以弹掉一个 0。
- 否则压一个 0,等待把它们弹掉。
遇到一个 1 的时候:
- 如果栈顶是 0,那么最优方案是操作一次(001, 101 都是操作一次更优,因为中间那个 0 无论如何我们都想把它删掉,如果留着 1 去干后面的事浪费的只能是 1),所以弹掉一个 0。
- 否则压一个 1。
最后只要栈内 1 的个数不少于 0 的个数即可。(在 01 交界处,每次消耗掉两个 1 和一个 0 变成一个 1。注意到最后栈中剩下奇数个数,所以 0 的个数和 1 的个数不相等)
注意到栈中 0 的个数永远不超过 2,且 1 的个数不减。所以如果 1 的个数大于 2,可以看成就是 2。
这样栈就只剩九种状态,可以 DP 了。
由于毕姥爷给我们讲这题时上了个自动机(当时完全掉线),所以这里也写了个自动机的样子(看起来高档一点)
时间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=300030,mod=1000000007,to[9][2]={{1,3},{2,0},{1,1},{4,6},{5,3},{4,4},{7,6},{8,6},{7,7}};
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,f[maxn][9],ans;
char s[maxn];
int main(){
scanf("%s",s+1);
n=strlen(s+1);
f[0][0]=1;
FOR(i,0,n-1) FOR(j,0,8){
if(s[i+1]!='1') f[i+1][to[j][0]]=(f[i+1][to[j][0]]+f[i][j])%mod;
if(s[i+1]!='0') f[i+1][to[j][1]]=(f[i+1][to[j][1]]+f[i][j])%mod;
}
FOR(i,3,8) if(i!=5) ans=(ans+f[n][i])%mod;
printf("%d\n",ans);
}
[AGC022F] Checkers
[AGC023D] Go Home
[AGC023E] Inversions
[AGC023F] 01 on Tree
[AGC024D] Isomorphism Freak
[AGC024E] Sequence Growing Hard
[AGC024F] Simple Subsequence Problem
[AGC025D] Choosing Points
[AGC025E] Walking on a Tree
[AGC025F] Addition and Andition
[AGC026D] Histogram Coloring
少数我自己能想出来的集训队作业(
而且还比大众做法快(
本题有很多做法,这里是一个比较难想(?),比较难写,但是快的做法。
首先注意到如果相邻两个方块同色,那么包含它的一个 \(4\times 4\) 的正方形的剩下两个方块只有一种涂法,而且这两个方块的颜色一样,都是前两个方块的颜色的反。
如果相邻两个方块不同色,那么包含它的一个 \(4\times 4\) 的正方形的剩下两个方块有两种涂法,只需要两个方块颜色不同。
令 \(f_{l,r,a,b,x}\) 表示考虑 \([l,r]\) 这段区间,如果把 \(\min\limits_{l\le i\le r} h_i\) 这个高度看成 \(1\) (也就是砍掉最小值以下的),左下角颜色是 \(a\),右下角颜色是 \(b\),最下面一排有没有两个相邻的颜色一样,的方案数。边界 \(l=r\) 自己各种考虑,答案是所有 \(f_{1,n,a,b,x}\) 的和。
转移,拎出 \([l,r]\) 里第一个取到最小值的位置,会将 \([l,r]\) 断成至多两段。最后 \(f_{l,r}\) 就是由这些段往下拉几行和一个高为 \(1\) 的列合并起来。
下拉一个区间,合并两个区间,都能大力枚举。
并不是所有区间都有用。建个笛卡尔树,就只有结点那些区间有用,共 \(O(n)\) 个。建笛卡尔树也可以 \(O(n)\)。
总时间复杂度 \(O(n)\),带个 \(30+\) 的常数。
最近没事干把代码精细实现了,应该是 \(O(n)\) 的。忽略快速幂吧不想改了,反正不是瓶颈
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=111111,mod=1000000007,inv2=500000004;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
int x=0,f=0;char ch=getchar();
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,h[maxn],ls[maxn],rs[maxn],stk[maxn],tp,rt,dp[maxn][2][2][2],tmp[2][2][2],ans;
inline int qpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;
return ans;
}
void go_lower(int f[][2][2],int cnt){
if(!cnt) return;
int g[2][2][2];
MEM(g,0);
FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) if(c){
if(cnt%2==0) g[a][b][c]=f[a][b][c];
else g[a^1][b^1][c]=f[a][b][c];
}
else{
int x=qpow(2,cnt-1);
g[a][b][c]=(g[a][b][c]+1ll*f[a][b][c]*x)%mod;
g[a^1][b^1][c]=(g[a^1][b^1][c]+1ll*f[a][b][c]*x)%mod;
}
FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) f[a][b][c]=g[a][b][c];
}
void merge(int f[][2][2],int g[][2][2]){
int h[2][2][2];
MEM(h,0);
FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) FOR(d,0,1) FOR(e,0,1) FOR(f_,0,1){
int to=c|f_|(b==d);
h[a][e][to]=(h[a][e][to]+1ll*f[a][b][c]*g[d][e][f_])%mod;
}
FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) f[a][b][c]=h[a][b][c];
}
void dfs(int x){
if(ls[x]){
dfs(ls[x]);
go_lower(dp[ls[x]],h[ls[x]]-h[x]);
FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) dp[x][a][b][c]=dp[ls[x]][a][b][c];
merge(dp[x],tmp);
}
else dp[x][0][0][0]=dp[x][1][1][0]=1;
if(rs[x]){
dfs(rs[x]);
go_lower(dp[rs[x]],h[rs[x]]-h[x]);
merge(dp[x],dp[rs[x]]);
}
}
int main(){
n=read();
FOR(i,1,n) h[i]=read();
FOR(i,1,n){
while(tp && h[i]<h[stk[tp]]) ls[i]=stk[tp--];
if(!tp) rt=i;
else rs[stk[tp]]=i;
stk[++tp]=i;
}
tmp[0][0][0]=tmp[1][1][0]=1;
dfs(rt);
go_lower(dp[rt],h[rt]-1);
FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) ans=(ans+dp[rt][a][b][c])%mod;
printf("%d\n",ans);
return 0;
}
[AGC026E] Synchronized Subsequence
[AGC026F] Manju Game
[AGC027D] Modulo Matrix
[AGC027E] ABBreviate
[AGC027F] Grafting
[AGC028C] Min Cost Cycle
[AGC028D] Chords
[AGC028E] High Elements
[AGC028F] Reachable Cells
[AGC029C] Lexicographic constraints
一上来感觉挺可做,想了个不知道什么鬼,写了一半被告知这题是模拟。
然后又发现自己想的假了。然后发现这个模拟似乎很显然(
怎样才能避免把模拟想复杂啊(
先看看字符集大小是 \(1\) 可不可行(不然下面还要判很多东西)。当且仅当序列严格递增。
二分一下,用栈维护所有字符不为最小的字符的位置(和这个位置上的字符)。
然后从左往右扫:
- 若 \(a_i>a_{i-1}\),加一堆最小的字符即可。所以栈不变。
- 否则,先把栈顶 \(>a_i\) 的位置弹出。然后 \(a_i\) 这个位置的字符需要变大一个。
- 若没有超出字符集大小,那就好。
- 若超出字符集大小,则这个位置设为最小的字符,前一个位置需要变大一个。如果还不行就继续。
- 若到最后要修改第 0 个位置,那就是不行,否则可以。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=200020,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
char ch=getchar();ll x=0,f=0;
while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return f?-x:x;
}
int n,a[maxn];
vector<PII> v;
bool add(int x,int upr){
if(!x) return false;
if(!v.empty()) assert(x>=v.back().first);
if(!v.empty() && x==v.back().first){
v.back().second++;
if(v.back().second>upr){
v.pop_back();
return add(x-1,upr);
}
}
else v.PB(MP(x,2));
return true;
}
bool check(int upr){
v.clear();
FOR(i,1,n) if(a[i]<=a[i-1]){
while(!v.empty() && v.back().first>a[i]) v.pop_back();
if(!add(a[i],upr)) return false;
}
return true;
}
int main(){
n=read();
FOR(i,1,n) a[i]=read();
bool flag=true;
FOR(i,1,n-1) flag&=a[i]<a[i+1];
if(flag) return puts("1"),0;
int l=2,r=n;
while(l<r){
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
}