被彼此笼罩 任歌声将我们缠绕 立下誓言后再自嘲 重复仲夏夜的舞蹈 吞下这毒药
test14
占卜divination
设 \(c_i\) 表示查询 \([i-1,i]\) 的次数,显然如果 \(c_i\geq 4\) 那么可以确定 \(s_{i-1}/s_i\)。
对 \(c_{l-1}=c_{r+1}=0,c_{l\to r}\neq 0\) 的一段考虑合法性。首先考虑开头的贡献,\(1\) 没用 \(2/3\) 可以确定一位,然后考虑怎么接前面的贡献,如果前面那一位可以确定那么 \(2/3\) 可以解决、\(1\) 可以确定新的位,如果前面那一位不确定看作新的开头。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=300005;
int T, n, m, tot, x[N], cnt[N], f[N];
bool mian() {
while(tot>0) f[tot--]=0;
cin >> n >> m;
up(i,1,m) cin >> x[i], ++x[i];
sort(x+1,x+1+m);
up(i,1,m) {
if(i==1||x[i]!=x[i-1]) x[++tot]=x[i], cnt[tot]=1;
else ++cnt[tot];
}
up(i,1,tot) {
if(cnt[i]>=4) return 1;
if(f[i-1]&&x[i-1]+1==x[i]) {
if(cnt[i]>1) return 1;
f[i]=1;
}
else f[i]=(cnt[i]>1);
}
return 0;
}
signed main() {
// freopen("1.txt","r",stdin);
freopen("divination.in","r",stdin);
freopen("divination.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) if(mian()) cout << "Yes\n"; else cout << "No\n";
return 0;
}
集合set
忘了怎么发现的但是就是可以发现对于 \(a\) 的合法条件是 \(\gcd\{a_i-a_{i-1}\}\neq 2^k\),这样子钦定一个端点就只有 \(\log n\) 种 \(\gcd\) 惹,暴力做就好了。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=200005;
int T, n, m, len, a[N], Ans, f[N], g[N], up[N];
void mian() {
cin >> n, Ans=n, m=0;
up(i,1,n) cin >> a[i];
dn(i,n,2) a[i]=abs(a[i]-a[i-1]);
up(i,2,n) while(a[i]&&a[i]%2==0) a[i]/=2;
up(i,2,n) {
int sav=Ans;
up(u,1,m) g[u]=__gcd(g[u],a[i]);
f[++m]=i, g[m]=a[i];
len=0;
up(u,1,m) if(u==1||g[u]!=g[u-1]) f[++len]=f[u], g[len]=g[u];
m=len;
if(g[1]==1) {
if(m==1) Ans+=i-1;
else Ans+=f[2]-2;
}
if(g[m]==0) Ans+=i-f[m]+1;
}
cout << Ans << '\n';
}
signed main() {
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) mian();
return 0;
}
遗迹heritage
考虑 \(a\to b\) 的过程,对于数值 \(v\) 显然只有更小的数值有影响,且对于 \(v\) 能影响到的都希望 \(v\) 存在,那么从大到小删是不劣的。然后发现希望比要被删除的 \(v\) 小的全都在 \(v\) 的左边或者右边。
考虑一个过程,从小到大还原 \(a\),对于要删除的 \(v\),插到序列左边或者右边,对于不用删除的 \(v\),插到前缀或者后缀里面可以插的地方。那么我们可以设计一个 \(dp[i][l][r]\) 表示考虑了 \(1,\dots,i\) 左/右连着 \(l/r\) 个要删除的数的方案数,然后直接 dp 出去,要搞点组合数。
但是我们还可以学习更简洁的做法,默认不删除的原本就在,再依次加入要删除的数,这样子维护前后缀有多少个空之后可以简单转移。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=505, P=1e9+7;
int n, m, b[N], pos[N], f[2][N][N], Ans;
inline void add(int &a,int b) { a=(a+b)%P; }
signed main() {
freopen("heritage.in","r",stdin);
freopen("heritage.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m, --n;
up(i,1,m) cin >> b[i], pos[--b[i]]=i;
if(!m) { cout << 0 << '\n'; return 0; }
if(pos[0]) f[0][pos[0]][pos[0]+1]=1;
else up(i,1,m+1) f[0][i][i]=1;
up(i,1,n) {
int now=i&1, pre=now^1;
memset(f[now],0,sizeof(f[now]));
if(pos[i]) {
up(l,1,m+1) up(r,l,m+1) {
add(f[now][min(l,pos[i])][max(r,pos[i]+1)],f[pre][l][r]);
}
}
else {
up(r,1,m+1) {
int sum=0;
dn(l,r,1) {
add(sum,f[pre][l][r]);
add(f[now][l][r],sum);
}
}
up(l,1,m+1) {
int sum=0;
up(r,l,m+1) {
add(sum,f[pre][l][r]);
add(f[now][l][r],sum);
}
}
}
}
cout << (f[n&1][1][m+1]%P+P)%P << '\n';
return 0;
}
博弈论game
倒序考虑 dp,设 \(f_{i,j}\) 表示考虑了 \(n,\dots,i\) 轮、有 \(j\) 个石头的胜负手。\(f_{i,}\) 是输赢交替的一段一段再拼一段平局后缀,不妨考虑维护变化的位置。然后 \(f_{i+1,}\to f_{i,}\) 的转移是隔着 \(+l/r_{i}\),打上 $\Delta $ 之后只需要去删除跨越的地方,删除的点有哪些可以用维护 rmq 的数据结构解决,删除本身可以用双向链表维护。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=200005;
int n, m, q, tot, l[N], r[N], val[N], pre[N], nxt[N], Ans, hd, dl, dr, ans[N];
struct node {
int i, v;
node(int I,int V) { i=I, v=V; }
bool operator<(const node rhs) const {
if(v==rhs.v) return i<rhs.i;
return v<rhs.v;
}
}; set<node> ql, qr;
/*
1. stl 的 swap 是 O(1) 的
2. 维护区间左端点比较好(就是维护左闭右开,不用 +-1)
3. 写贡献看始终,不要随便 yy 中间贡献
4. 平局贡献是自然的
*/
signed main() {
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
// freopen("1.txt","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
up(i,1,n) cin >> l[i] >> r[i];
hd=1, m=2, nxt[1]=2, pre[2]=1, val[2]=l[n];
ql.insert(node(1,l[n]));
dn(u,n-1,1) {
dl+=l[u], dr+=r[u];
// i -> j : i+dr>j+dl , dr-dl>j-i
while(qr.size()&&dr-dl>=qr.begin()->v) {
int i=qr.begin()->i, j=nxt[i];
qr.erase(qr.begin());
if(!nxt[j]) { nxt[i]=0; continue; }
if(!pre[i]) hd=nxt[j];
nxt[pre[i]]=nxt[j], pre[nxt[j]]=pre[i];
if(pre[i]) ql.erase(node(pre[i],val[i]-val[pre[i]]));
if(nxt[j]) ql.erase(node(j ,val[nxt[j]]-val[j]));
if(pre[i]&&nxt[j]) ql.insert(node(pre[i],val[nxt[j]]-val[pre[i]]));
}
swap(dl,dr), swap(ql,qr);
++m, pre[hd]=m, nxt[m]=hd;
val[m]=-dl, ql.insert(node(m,val[hd]-val[m])), hd=m;
}
for(int i=hd; i; i=nxt[i]) {
ans[++tot]=val[i];
if(tot%2==1) ans[tot]+=dl; else ans[tot]+=dr;
}
cin >> q;
while(q--) {
int x;
cin >> x;
int i=upper_bound(ans+1,ans+1+tot,x)-ans-1;
if(i==tot) cout << "Draw\n";
else {
if(i&1) cout << "Bob\n";
else cout << "Alice\n";
}
}
return 0;
}

浙公网安备 33010602011771号