1. 3721 Smuggling Marbles

大意: 给定$n+1$节点树, $0$为根节点, 初始在一些节点放一个石子, 然后按顺序进行如下操作.

  • 若$0$节点有石子, 则移入盒子
  • 所有石子移向父亲节点
  • 把所有不少于$2$个石子的节点的石子丢掉
  • 若树上还有石子,则返回第一步

对于所有$2^{n+1}$种初始放石子的方案, 求出最后盒子中石子总数之和.

长链剖分, 这道以后再写

2. 3727 Prefix-free Game

两个串$s,t$合法要满足 $s$不为$t$的前缀且$t$不为$s$的前缀.一个字符串集合合法要求满足 每个串长度范围$[1,L]$, 每个串只由$01$组成, 任意两串合法.给定合法字符串集$S$, 两人轮流操作, 每次添加一个字符串, 要求添加后$S$仍然合法, 不能操作则输. 求最后胜负情况.

假设初始$S$为空的情况. 那么初始状态可以看做两棵深度为$L$的满二叉树(因为不能取空串).

每步操作相当于选一个节点$x$, 然后删去$x$的子树以及$x$到根的链. 可以发现删完一定分裂成多颗满二叉树, 所以这样状态就只与二叉树的深度有关, 可以得到

$$SG_{x}=mex\{0,SG_{x-1},SG_{x-1}\oplus SG_{x-2},...,SG_{x-1}\oplus ...\oplus SG_{1}\}$$

打表可以发现$SG_{x}=lowbit(x)$.

所以对于给定初始字符串集合$S$的情况, 用$trie$模拟求出初始$SG$值即可.

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int N = 1e6+;
int n, T, tot;
ll L, ans;
struct {int ch[];} tr[N<<];
char s[N]; void add(int &o, char *s) {
if (!o) o = ++tot;
if (*s) add(tr[o].ch[*s==''],s+);
}
void dfs(int o, ll d) {
if (!o) ans ^= d&-d;
else dfs(tr[o].ch[],d-),dfs(tr[o].ch[],d-);
} int main() {
scanf("%d%lld", &n, &L);
REP(i,,n) scanf("%s", s),add(T,s);
dfs(T,L+);
puts(ans?"Alice":"Bob");
}

3. 3939 Strange Nim

大意: $n$堆石子, 第$i$堆初始$A_i$, 有一个系数$K_i$, 每次操作假设第$i$堆有$X$个石子, 那么可以拿走的石子范围为$[1,\lfloor\frac{X}{K_i}\rfloor]$. 两人轮流操作, 求最后胜负情况.

打表可以发现$x\%k==0$时$, sg(x,k)=\lfloor\frac{x}{k}\rfloor$.

否则$sg(x,k)=sg(x-\lceil\frac{x}{k}\rceil,k)$.

通过同时减去相同的$\lceil\frac{x}{k}\rceil$来优化, 复杂度就为$O(\sqrt{k})$

#include <iostream>
#include <cstdio>
using namespace std; int sg(int x, int k) {
if (x%k==) return x/k;
int t = x/k+;
return sg(x-(x%k+t-)/t*t,k);
} int main() {
int n;
scanf("%d", &n);
int ans = ;
while (n--) {
int a, k;
scanf("%d%d", &a, &k);
ans ^= sg(a,k);
}
puts(ans?"Takahashi":"Aoki");
}

4. 2044 Teleporter

大意: $n$个点, 点$i$后继为$a_i$, 每个点都可以到达$1$, 求最少修改多少后继使得每个点恰好走$k$步能到达点$1$.

$a_1$必须为$1$, 否则$1$和$a_1$一定不能满足条件, 然后$dfs$从叶子往上贪心.

#include <iostream>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std; const int N = 1e6+;
int n, k, ans, a[N], f[N];
vector<int> g[N]; void dfs(int x) {
f[x] = ;
for (int y:g[x]) dfs(y), f[x] = max(f[x], f[y]+);
if (f[x]==k&&a[x]!=) ++ans,f[x]=;
}
int main() {
scanf("%d%d", &n, &k);
REP(i,,n) scanf("%d", a+i);
ans = a[]!=;
a[] = ;
REP(i,,n) g[a[i]].pb(i);
dfs();
printf("%d\n", ans);
}

5. 2000 Leftmost Ball

大意: 给定$n$个颜色的球, 每种$k$个, 任意排列后将每种球第一个颜色染为$0$, 求能得到多少种序列.

设$f_{i,j}$为当前放了$i$个$0$, $j$种颜色的方案数.

从左到右枚举最前面的空位应该放白球还是放彩球, 若放彩球则将剩余彩球直接分配下去.

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int P = 1e9+;
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
const int N = , M = 4e6+;
int n, k, dp[N][N];
int fac[M], ifac[M];
int C(int n, int m) {
if (n<m) return ;
return (ll)fac[n]*ifac[m]%P*ifac[n-m]%P;
}
int main() {
fac[]=;
REP(i,,M-) fac[i]=(ll)fac[i-]*i%P;
ifac[M-]=inv(fac[M-]);
PER(i,,M-) ifac[i]=(ll)ifac[i+]*(i+)%P;
scanf("%d%d", &n, &k);
if (k==) return puts(""),;
dp[][] = ;
REP(i,,n) REP(j,,i) {
dp[i+][j] = (dp[i+][j]+dp[i][j])%P;
dp[i][j+] = (dp[i][j+]+(ll)C(n*k-j*(k-)-i-,k-)*dp[i][j])%P;
}
int ans = (ll)dp[n][n]*fac[n]%P;
printf("%d\n", ans);
}

6. 2020 Unbalanced

大意: 若一个串满足长度不少于$2$且超过一半的字符相同, 则称它为不平衡串. 给定串$s$, 要求输出$s$的任意一个不平衡子串.

众数的套路题. 枚举字符$x$作为众数的情况, $x$看做$1$, 其余字符看做$-1$, 那么就等价于找一个和大于零的区间.

#include <iostream>
#include <cstdio>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define x first
#define y second
using namespace std;
typedef pair<int,int> pii; const int N = 1e6+;
int n, f[N][];
pii mi[];
char s[N]; int main() {
scanf("%s", s+);
n = strlen(s+);
REP(i,,n) {
memcpy(f[i],f[i-],sizeof f[]);
REP(j,'a','z') {
if (s[i]==j) ++f[i][j-'a'];
else --f[i][j-'a'];
}
if (i>) {
REP(j,,) if (f[i][j]-mi[j].x>) {
return printf("%d %d\n",mi[j].y+,i),;
}
}
REP(j,,) mi[j] = min(mi[j], pii(f[i-][j],i-));
}
puts("-1 -1");
}

看了其他人题解发现有更简便做法, 因为只需要找一个, 所以直接判断是否存在$XYX$或$XX$这种即可.

7. 2021 Children and Candies

大意: $n$个人分$C$块糖. 定义函数$f(x_1,...,x_n)$, 第$i$个人若分$a$块糖, 则高兴度为$x_i^a$, $f$的值为所有人高兴度的乘积. 给定序列$A,B$, 求$\sum\limits_{x_1=A_1}^{B_1}\sum\limits_{x_2=A_2}^{B_2}\cdots\sum\limits_{x_n=A_n}^{B_n}f(x_1,x_2,...,x_n)$

简单dp题, 设$dp_{i,x}$为前$i$个人分$x$块糖的答案, 可以得到$dp_{i,x}=\sum\limits_{A_i\le k\le B_i}\sum\limits_{y\le x}dp_{i-1,y}k^{x-y}$.

然后前缀优化一下.

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+, INF = 0x3f3f3f3f;
const int N = ;
int n, c, a[N], b[N], dp[N][N];
int po[N][N], sum[N][N]; int main() {
REP(i,,N-) {
po[i][] = ;
REP(j,,N-) po[i][j] = (ll)po[i][j-]*i%P;
}
REP(i,,N-) {
sum[i][] = ;
REP(j,,N-) sum[i][j] = (sum[i][j-]+po[j][i])%P;
}
cin>>n>>c;
REP(i,,n) cin>>a[i];
REP(i,,n) cin>>b[i];
dp[][] = ;
REP(i,,n) REP(x,,c) REP(y,,x) {
int ret = sum[x-y][b[i]]-sum[x-y][a[i]-];
dp[i][x] = (dp[i][x]+(ll)dp[i-][y]*ret)%P;
}
int ans = dp[n][c];
if (ans<) ans += P;
printf("%d\n", ans);
}

8. 2022  Unhappy Hacking

大意: 键盘有三个键$0,1$和退格, 求按$n$次以后得到字符串$s$的方案数

显然答案只与$n$有关, 所以求出能得到的所有串的方案数最后除以$2^{|s|}$即可

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+;
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
const int N = ;
int n, m, dp[N][N];
char s[N];
void add(int &x, ll y) {x = (x+y)%P;} int main() {
scanf("%d%s", &n, s+);
m = strlen(s+);
dp[][] = ;
REP(i,,n) REP(j,,i) if (dp[i][j]) {
add(dp[i+][j+],*dp[i][j]);
add(dp[i+][max(j-,)],dp[i][j]);
}
int ans = (ll)dp[n][m]*inv(qpow(,m))%P;
printf("%d\n", ans);
}

9. 2070 Card Game for Three

大意: $A,B,C$三个人初始$n,m,k$张牌, 每张牌上是三个人名字. 每个人出牌顺序固定,  每轮出一张牌, 然后牌上写的人接着出. 谁先出完谁赢. 对于所有$3^{n+m+k}$中出牌顺序, 求先手胜利方案数.

显然对于一个长度为$x$的出牌序列, 对应$3^{n+m+k-x}$种方案.

只需要考虑出$n$张$A$,$i$张$B$,$j$张$C$, 且最后一张为$A$的方案数, 有

$$\begin{align} ans &=\sum\limits_{i=0}^m\sum\limits_{j=0}^k 3^{m+k-i-j}\frac{(n+i+j-1)!}{(n-1)!i!j!} \notag \\ &= \frac{3^{m+k}}{(n-1)!}\sum\limits_{i=0}^m\frac{3^{-i}}{i!}\sum\limits_{j=0}^k\frac{3^{-j}}{j!}(n+i+j-1)! \notag \end{align}$$

记$f(i)=\sum\limits_{j=0}^k\frac{3^{-j}}{j!}(n+i+j-1)!$

$$\begin{align} f(i+1)-f(i) &= \sum\limits_{j=0}^k\frac{3^{-j}}{j!}(n+i+j-1)!(n+i+j-1) \notag \\ &= f(i)(n+i-1)+\sum\limits_{j=0}^k \frac{3^{-j}j}{j!}(n+i+j-1)! \notag \\ &= f(i)(n+i-1)+3^{-1}(f(i+1)-\frac{3^{-k}}{k!}(n+i+k)!) \notag \end{align}$$

所以

$$f(i+1)=\frac{3}{2}f(i)(n+i)-\frac{3^{-k}}{2}\frac{(n+i+k)!}{k!}$$

然后就可以$O(n)$做了

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int N = 1e6+, P = 1e9+, inv2 = (P+)/;
int inv(int x){return x<=?:inv(P%x)*(P-(ll)P/x)%P;}
int n,m,k,fac[N],ifac[N],po[N],ipo[N];
int main() {
ifac[]=fac[]=po[]=ipo[]=;
REP(i,,N-) {
fac[i]=fac[i-]*(ll)i%P;
po[i]=po[i-]*3ll%P;
}
ifac[N-]=inv(fac[N-]),ipo[N-]=inv(po[N-]);
PER(i,,N-) {
ifac[i]=ifac[i+]*(i+1ll)%P;
ipo[i]=ipo[i+]*3ll%P;
}
cin>>n>>m>>k;
int ans = , ret = ;
REP(j,,k) ret = (ret+(ll)ipo[j]*ifac[j]%P*fac[n+j-])%P;
REP(i,,m) {
ans = (ans+(ll)ipo[i]*ifac[i]%P*ret)%P;
ret = 3ll*inv2%P*ret%P*(n+i)%P;
ret = (ret-(ll)ipo[k]*inv2%P*fac[n+i+k]%P*ifac[k])%P;
}
ans = (ll)ans*po[m+k]%P*ifac[n-]%P;
if (ans<) ans += P;
printf("%d\n", ans);
}

10. 4257 Factorization

大意: 给定$n,m$, 求长为$n$的序列,乘积等于$m$的方案数.

记$f_{i,x}$为$i$个数乘积$x$的方案, 就有$f_{i,x}=\sum\limits_{ab=x}f_{i-1,a}f_{1,b}$

所以$f$就为积性函数, 有$f_{n,p^k}=\binom{n+k-1}{k}$, 然后相乘即可.

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
const int N = 1e6+;
int n, m, fac[N], ifac[N];
int C(int n, int m) {
if (n<m) return ;
return fac[n]*(ll)ifac[m]%P*ifac[n-m]%P;
}
int main() {
fac[]=;
REP(i,,N-) fac[i]=(ll)fac[i-]*i%P;
ifac[N-]=inv(fac[N-]);
PER(i,,N-) ifac[i]=(ll)ifac[i+]*(i+)%P;
scanf("%d%d", &n, &m);
int ans = ;
for (int i=; i*i<=m; ++i) {
int cnt = ;
while (m%i==) m/=i,++cnt;
ans = (ll)ans*C(n+cnt-,cnt)%P;
}
if (m>) ans = (ll)ans*n%P;
printf("%d\n", ans);
}

11. 3606 Combination Lock

大意:给定字符串$S$, $n$种操作$(L,R)$, 将$s[L...R]$字符加$1$, $z$变为$a$. 每种操作可以执行任意次, 可以按任意顺序执行, 求能否变为回文串.

区间加可以差分为$++c[l],--c[r+1]$, 回文限制相当于所有对称位置的差分之和为$0$.

连边看每个连通块的和是否为$0$即可.

#include <iostream>
#include <cstdio>
#include <queue>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define pb push_back
using namespace std;
const int N = 1e6+;
int n, m, sum, c[N], vis[N];
char s[N];
vector<int> g[N];
void add(int x, int y) {
g[x].pb(y),g[y].pb(x);
}
void dfs(int x) {
if (vis[x]) return;
vis[x] = ;
sum = (sum+c[x])%;
for (int y:g[x]) dfs(y);
}
int main() {
cin>>s+;
m = strlen(s+);
REP(i,,m+) {
c[i]=(s[i]-s[i-])%;
add(i,m+-i);
}
cin>>n;
while (n--) {
int l,r;
cin>>l>>r;
add(l,r+);
}
REP(i,,m+) {
sum = ;
dfs(i);
if (sum) return puts("NO"),;
}
puts("YES");
}

12. 3605 Zabuton

大意: $n$个人, 初始高度为$0$, 若当前高度不超过$h_i$, 那么第$i$个人可以叠上去, 使高度增加$p_i$, 求最多能叠多少个人.

贪心按$h+p$排序, 然后$dp$一定最优, 考虑证明.

对于两个人$(h_a,p_a),(h_b,p_b)$, 假设$a,b$之前的和为$x$.

若$a$排在$b$前, 有$x\le min(h_a,h_b-p_a)$, 否则有$x\le min(h_b,h_a-p_b)$.

$a$在前比$b$在前更优等价于$min(h_a,h_b-p_a)>min(h_b,h_a-p_b)$

去掉$min$可以得到$h_a+p_a<h_b+p_b$.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <math.h>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <string.h>
#include <bitset>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 5e3+;
int n;
ll dp[N];
struct _ {
int x,y;
bool operator < (const _ &rhs) const {
return x+y<rhs.x+rhs.y;
}
} a[N]; int main() {
cin>>n;
REP(i,,n) cin>>a[i].x>>a[i].y;
sort(a+,+a+n);
memset(dp,0x3f,sizeof dp);
dp[] = ;
REP(i,,n) {
PER(j,,i-) if (dp[j]<=a[i].x) {
dp[j+]=min(dp[j+],dp[j]+a[i].y);
}
}
PER(i,,n) if (dp[i]<=1e16) return printf("%d\n",i),;
}

13. 2292 Division into Two

大意: 给定序列, 求划分为两个集合$X,Y$, 满足$X$中任意两数之差的绝对值不少于$A$, $Y$中任意两数之差的绝对值不少于$B$, 求方案数.

$DP$好题.

首先$O(n^2)$的$DP$很容易想, 只要枚举上次出现位置即可.

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+, INF = 0x3f3f3f3f;
const int N = 1e3+;
int n, dp[][][N];
ll a,b,s[N]; void add(int &x, int y) {x+=y;if (x>=P)x-=P;}
int main() {
cin>>n>>a>>b;
REP(i,,n) cin>>s[i];
s[] = -1e18;
int cur = , ans = ;
dp[][][] = ;
//dp[i][z][j]
//z为0, X上个数位置在i, Y上个数位置在j
//z为1, X上个数位置在j, Y上个数位置在i
REP(i,,n) {
cur ^= ;
memset(dp[cur],,sizeof dp[cur]);
REP(j,,i-) REP(z,,) {
int &r = dp[!cur][z][j];
if (!r) continue;
if (!z&&s[i]-s[i-]>=a||z&&s[i]-s[i-]>=b) {
add(dp[cur][z][j],r);
if (i==n) add(ans,r);
}
if (!z&&s[i]-s[j]>=b||z&&s[i]-s[j]>=a) {
add(dp[cur][!z][i-],r);
if (i==n) add(ans,r);
}
}
}
printf("%d\n", ans);
}

考虑$O(n)$的做法, 记$dp_i$为集合$Y$取第$i$个数的方案数, 可转移的$j$要满足

$$s_i-s_j\ge B$$

$$s_k-s_{k-1}\ge A,k\in [j+2,i-1]$$

所以$j$是一段连续的区间, 可以前缀和优化一下即可$O(n)$

#include <iostream>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int P = 1e9+;
const int N = 1e6+;
int n,f[N];
ll s[N],a,b; int main() {
cin>>n>>a>>b;
if (a>b) swap(a,b);
REP(i,,n) cin>>s[i];
REP(i,,n) if (s[i]-s[i-]<a) return puts(""),;
f[] = ;
int l=, r=;
s[n+] = 2e18;
REP(i,,n+) {
while (r<i-&&s[i]-s[r+]>=b) ++r;
if (l<=r) f[i]=(f[r]-(l?f[l-]:))%P;
f[i] = (f[i]+f[i-])%P;
if (i>&&s[i]-s[i-]<a) l=i-;
}
int ans = (f[n+]-f[n])%P;
if (ans<) ans+=P;
printf("%d\n",ans);
}

14. 3673 NRE

大意: 序列$a$初始全$0$, 给定01序列$b$, $q$个操作$(l,r)$, 表示把$a_l,...,a_r$全改为$1$. 求选择一些操作, 使得最后$a,b$的汉明距离最小

刚开始想着枚举位置$dp$, $wa$了一发才发现转移是不对的. 正解是把所有区间按左端点排序, 记$dp_i$为只考虑前$i$个区间的答案, 那么有

$$dp_i =   \begin{cases} dp_j+s_0[r_i]-s_0[r_j],  & l_j\le l_i\le r_j\le r_i \\ dp_j+s_0[r_i]-s_0[l_i-1]+s_1[l_i-1]-s_1[r_j], & r_j<l_i\end{cases}$$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 2e5+;
int n, b[N], s[][N];
vector<int> g[N];
int c0[N<<],c1[N<<],pos[N<<];
void build(int o, int l, int r) {
c0[o] = c1[o] = INF;
if (l==r) pos[l] = o;
else build(ls),build(rs);
}
int qry(int *c, int o, int l, int r, int ql, int qr) {
if (ql<=l&&r<=qr) return c[o];
if (mid>=qr) return qry(c,ls,ql,qr);
if (mid<ql) return qry(c,rs,ql,qr);
return min(qry(c,ls,ql,qr),qry(c,rs,ql,qr));
}
void upd(int *c, int o, int v) {
o = pos[o];
c[o] = min(c[o], v);
while (o>>=) c[o] = min(c[lc],c[rc]);
}
int main() {
scanf("%d",&n);
REP(i,,n) {
scanf("%d",b+i);
s[][i]=s[][i-];
s[][i]=s[][i-];
++s[b[i]][i];
}
build(,,n),upd(c0,,),upd(c1,,);
int q;
scanf("%d",&q);
while (q--) {
int l, r;
scanf("%d%d", &l, &r);
g[l].pb(r);
}
int ans = s[][n];
REP(l,,n) for (int r:g[l]) {
int x = qry(c0,,,n,l,r)+s[][r];
x = min(x, qry(c1,,,n,,l-)+s[][r]-s[][l-]+s[][l-]);
upd(c0,r,x-s[][r]);
upd(c1,r,x-s[][r]);
ans = min(ans, x+s[][n]-s[][r]);
}
printf("%d\n", ans);
}

15. 3733 Papple Sort

大意: 给定字符串, 每次操作交换相邻字符, 求变成回文所需要最少操作数.

考虑每种字符的出现位置, 同种字符间显然不会产生交换, 那么每种字符相对位置是不变的, 假设出现位置为$p_1,p_2,...,p_r$, 那么最终$p_1$与$p_r$配对, $p_2$与$p_{r-1}$配对, 以此类推. 记$p_1,p_2,...,p_{r/2}$为$A$, 其余为$B$. 也就是说最优情况$A$全在左侧, $B$全在右侧. 可以发现对于每种局面, 固定$A$不动, 依次把$B$贪心放到$A$的对称位置一定最优. 所以贪心从左到右枚举, 每次遍历到$A$时, $A$的位置一定是在左半边(因为这个$A$是当前未匹配的位置最小的$A$), 把对应的$B$移到对应位置即可. 最后需要注意特判长度为奇数的情况, 需要把中间的字符留到最后再移动. 具体实现用树状数组模拟即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n, vis[N], c[N];
char s[N];
vector<int> g[N];
void add(int x, int v) {
for (; x<=n; x+=x&-x) c[x]+=v;
}
int qry(int x) {
int r = ;
for (; x; x^=x&-x) r+=c[x];
return r;
}
int main() {
scanf("%s",s+);
n = strlen(s+);
int d = ;
REP(i,,) g[i].clear();
REP(i,,n) c[i]=,vis[i]=;
REP(i,,n) s[i]-='a',d^=<<s[i],g[s[i]].pb(i);
if (d&(d-)) {cout<<-<<endl;continue;}
ll ans = ;
int now = n;
REP(i,,n) if (!vis[i]) {
int nxt = g[s[i]].back();
g[s[i]].pop_back();
int x = nxt-qry(nxt);
if (i==nxt) ans += max((n+)/-x,);
else ans += max(now---x,), add(nxt+,);
vis[nxt] = ;
}
printf("%lld\n", ans);
}

16. 1982 Arrays and Palindrome

大意: 有两个序列$a,b$, 保证$sum(a)=N,sum(b)=N$. 满足对于任意序列: 若前$a_1$个字符,接着$a_2$个字符,...都是回文串. 前$b_1$个字符, 接着前$b_2$个字符,...都是回文串. 那么这个序列所有字符相等. 现在给出$a$的一个排列$A$, 求恢复序列$a,b$.

奇数个数$>2$不成立, 否则把奇数放两侧, 让$b[1]=a[1]+1$,$b[m]=a[m]-1$,其余$b[i]=a[i]$即可.要注意特判$m=1$的情况, 和两个奇数都为$1$的情况

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n, m, a[N], b[N]; int main() {
scanf("%d%d", &n, &m);
vector<int> v[];
REP(i,,m) {
int t;
scanf("%d", &t);
v[t&].pb(t);
}
if (m==) {
if (n==) return puts("1\n1\n1"),;
return printf("%d\n%d\n%d %d\n",n,,,n-),;
}
if (v[].size()>) return puts("Impossible"),;
if (v[].size()==) {
a[] = v[][], a[m] = v[][];
if (a[]>a[m]) swap(a[],a[m]);
b[] = a[]+;
REP(i,,m-) {
a[i] = v[].back(), v[].pop_back();
b[i] = a[i];
}
b[m] = a[m]-;
}
else if (v[].size()==) {
a[] = v[][], b[] = a[]+;
REP(i,,m) {
a[i] = v[].back(), v[].pop_back();
b[i] = a[i];
}
b[m] = a[m]-;
}
else {
a[] = v[].back(), b[] = a[]+;
v[].pop_back();
REP(i,,m) {
a[i] = v[].back(), v[].pop_back();
b[i] = a[i];
}
b[m] = a[m]-;
}
REP(i,,m) printf("%d ",a[i]);hr;
if (!b[m]) --m;
printf("%d\n",m);
REP(i,,m) printf("%d ",b[i]);hr;
}

17. 1983 BBQ Hard

大意: $n$个套餐, 第$i$个套餐有一根钎子, $a_i$个牛肉, $b_i$个青椒. 钎子有标号, 牛肉青椒无标号. 求选出两个套餐组成串成烤肉串的方案. 具体可以看样例图解.

容易得到答案为$\sum\limits_{1\le i<j\le n}\frac{(a_i+a_j+b_i+b_j)!}{(a_i+a_j)!(b_i+b_j)!}$, 根据对称性转化为求$\sum\limits_{i=1}^n\sum\limits_{j=1}^n \frac{(a_i+a_j+b_i+b_j)!}{(a_i+a_j)!(b_i+b_j)!}$

可以发现答案为所有$(-a_i,-b_i)$走到$(a_j,b_j)$的格路径数, $S$连向每个点$(-a,-b)$, $(a,b)$连向$T$, $dp$求出$S$到$T$的路径条数即可

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = , M = , S = 2e5+;
int n, a[S], b[S], dp[N][N], fac[S], ifac[S];
void add(int &a, int b) {a+=b;if (a>=P) a-=P;}
int C(int n, int m) {
return (ll)fac[n]*ifac[m]%P*ifac[n-m]%P;
}
int main() {
fac[]=;
REP(i,,S-) fac[i]=(ll)fac[i-]*i%P;
ifac[S-]=inv(fac[S-]);
PER(i,,S-) ifac[i]=(ll)ifac[i+]*(i+)%P;
scanf("%d", &n);
REP(i,,n) scanf("%d%d",a+i,b+i),++dp[-a[i]+M][-b[i]+M];
REP(i,-M,M) REP(j,-M,M) if (dp[i+M][j+M]) {
add(dp[i+M+][j+M],dp[i+M][j+M]);
add(dp[i+M][j+M+],dp[i+M][j+M]);
}
int ans = ;
REP(i,,n) ans = (ans+dp[a[i]+M][b[i]+M])%P;
REP(i,,n) ans = (ans-C(*a[i]+*b[i],*a[i]))%P;
if (ans<) ans += P;
ans = (ll)ans*inv()%P;
printf("%d\n", ans);
}

18. 1984 Wide Swap

大意: 给定一个排列$p$, 每次操作选择$i,j$, 要求$j-i\ge k, abs(p_i-p_j)=1$, 然后交换$p_i,p_j$. 可以进行人一次操作, 求使排列$p$字典序最小方案.

agc的题都好有意思. 核心观察是可以用$pos_i$表示$i$在$p$中的位置, 那么就转化为每次交换相邻数, 要求最后$1$尽量在最前, 然后$2$尽量在最前, 以此类推. 这样就转化为[HNOI2015]菜肴制作, 建图拓扑即可. 不过这个题似乎直接用小根堆拓扑就能得到正确答案, 可能是因为这题建出的图的性质比较特殊.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n, k, a[N], pos[N], deg[N], f[N];
vector<int> g[N]; int main() {
scanf("%d%d", &n, &k);
REP(i,,n) scanf("%d", a+i);
REP(i,,n) pos[a[i]]=i;
set<int> L, R;
REP(i,,k) R.insert(a[i]);
REP(i,,n) g[i].clear(),deg[i]=;
//L维护<i的数在pos中的位置, R维护>i的数在pos中的位置
REP(i,,n) {
auto p = L.upper_bound(a[i]);
if (p!=L.end()) {
g[pos[*p]].pb(i);
++deg[i];
}
p = R.upper_bound(a[i]);
if (p!=R.end()) {
g[pos[*p]].pb(i);
++deg[i];
}
L.insert(a[i]), R.erase(a[i]);
if (i-k+>) L.erase(a[i-k+]);
if (i+k<=n) R.insert(a[i+k]);
}
priority_queue<int> q;
REP(i,,n) if (!deg[i]) q.push(i);
PER(i,,n) {
int u = q.top(); q.pop();
f[i] = u;
for (int v:g[u]) {
if (!--deg[v]) q.push(v);
}
}
REP(i,,n) a[f[i]] = i;
REP(i,,n) printf("%d\n",a[i]);
}

19. 1999 Candy Piles

大意: $n$堆石子, 两人轮流操作, 每次有两种选择: (1)把石子最多的堆的石子全拿走 (2)每个非空堆拿一个. 拿走最后一个的人输, 求最后胜负情况.

石子降序排序, 每堆看成一个矩形, 然后转成棋盘博弈问题. 可以发现一个对角线上的点状态相同, 求出初始位置所在对角线与所有矩形边界的交点判断一下即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n, a[N]; int main() {
scanf("%d", &n);
REP(i,,n) scanf("%d", a+i);
sort(a+,a++n,greater<int>());
REP(i,,n) {
int j = i;
while (j<n&&a[j+]==a[i]) ++j;
if ((j-a[j])%&&(i-<=a[j]&&a[j]<=j||a[j+]<=j&&j<=a[j])) return puts("First"),;
i = j;
}
puts("Second");
}

20. 1998 Stamp Rally

大意: 给定无向连通图, 每次询问两人分别从点$x,y$出发, 一共遍历$z$个点后结束, 求遍历到的边的最大标号的最小值

单询问的话可以直接从小到大添边, 当$x,y$所在连通块大小不少于$z$的时候就为答案. 多组询问套个整体二分即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 2e6+;
int n,m,q,now,ans[N];
pii a[N];
struct {int x,y,z,id;} e[N],q1[N],q2[N];
int fa[N],sz[N];
int find(int x) {while (fa[x]!=x) x=fa[x]; return x;}
vector<pair<int*,int> > L[N], R[N];
void init() {
REP(i,,m) {
int u = find(a[i].x), v = find(a[i].y);
if (u==v) continue;
if (sz[u]<sz[v]) swap(u,v);
L[i].emplace_back(sz+u,sz[u]);
L[i].emplace_back(fa+v,fa[v]);
sz[u] += sz[v], fa[v] = u;
R[i].emplace_back(sz+u,sz[u]);
R[i].emplace_back(fa+v,fa[v]);
}
now = m;
}
void add(int id) {
for (auto &t:R[id]) *t.x=t.y;
}
void del(int id) {
for (auto &t:L[id]) *t.x=t.y;
}
void solve(int s, int t, int l, int r) {
if (l==r) {REP(i,s,t) ans[e[i].id]=l;return;}
while (now<mid) add(++now);
while (now>mid) del(now--);
int l1 = , l2 = ;
REP(i,s,t) {
int a = find(e[i].x), b = find(e[i].y), cnt = sz[a];
if (a!=b) cnt += sz[b];
if (cnt>=e[i].z) q1[++l1] = e[i];
else q2[++l2] = e[i];
}
REP(i,,l1) e[s+i-]=q1[i];
REP(i,,l2) e[s+i-+l1]=q2[i];
if (l1) solve(s,s+l1-,l,mid);
if (l2) solve(s+l1,t,mid+,r);
}
int main() {
scanf("%d%d",&n,&m);
REP(i,,n) sz[i] = , fa[i] = i;
REP(i,,m) scanf("%d%d",&a[i].x,&a[i].y);
init();
scanf("%d", &q);
REP(i,,q) e[i].id=i,scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
solve(,q,,m);
REP(i,,q) printf("%d\n",ans[i]);
}

21. 2040 Best Representation

大意: 若一个字符串$x$满足长度至少为$1$, 对于任意串$y$,任意整数$k\le 2$, $x$不等于$y$重复$k$次, 那么$x$为好字符串. 若一个字符串的划分的每一部分都是好字符串, 那么这个划分为好划分. 给定字符串$w$, 求$w$最小的好划分, 以及最小好划分的个数.

最小好划分大小要么为$n$, 要么为$1$, 要么为$2$. 这样枚举间断点, $kmp$求一下循环节即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n;
char s[N], t[N];
int pre[N], suf[N];
void getfail(char *s, int *f, int n) {
int j = ;
REP(i,,n-) {
while (j&&s[i]!=s[j]) j=f[j];
if (s[i]==s[j]) ++j;
f[i+] = j;
}
} int main() {
scanf("%s", s+);
n = strlen(s+);
REP(i,,n) t[i] = s[n-i+];
getfail(s+,pre,n);
getfail(t+,suf,n);
int len = n%(n-pre[n])==?n/(n-pre[n]):;
if (len==n||len==) return printf("%d\n1\n",len),;
int ans = ;
REP(i,,n-) {
int x = i%(i-pre[i])==?i/(i-pre[i]):;
int y = (n-i)%(n-i-suf[n-i])==?(n-i)/(n-i-suf[n-i]):;
if (x==&&y==) ++ans;
}
printf("2\n%d\n", ans);
}

22. 2042 Colorful Slimes

大意: $n$种史莱姆, 抓第$i$种花费$a_i$时间, 可以花费$x$时间把已经抓到的史莱姆种类循环右移一下, 求最少时间抓完$n$种史莱姆.

假设循环右移$t$次, 那么相当于第$i$种史莱姆可以取$[i-t+1,i]$的最小值, 所以枚举右移次数即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 4e3+;
int n, x, a[N], b[N]; int main() {
scanf("%d%d", &n, &x);
REP(i,,n) scanf("%d", a+i),a[i+n]=a[i];
memset(b,0x3f,sizeof b);
ll ans = 1e18;
REP(j,,n) {
REP(i,,n) b[i]=min(b[i],a[i-j+n]);
ans = min(ans, accumulate(b+,b++n,0ll)+(ll)j*x);
}
printf("%lld\n", ans);
}

23. 2045 Salvage Robots

大意: 给定棋盘, 有一个出口, 一些点有机器人, 每次操作选一个方向, 让所有机器人向该方向走一格, 走出边界的机器人立即死亡, 走到出口的得救. 可以操作任意次, 求最多救多少个机器人.

范围很小, 可以直接暴力区间$dp$. 卡空间, 需要滚动一下数组

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = ;
short h, w, a[N][N];
char s[N][N];
short dp[N][N][N];
short C(int x1, int y1, int x2, int y2) {
if (x1>x2||y1>y2) return ;
--y1,--x1;
return a[x2][y2]-a[x1][y2]-a[x2][y1]+a[x1][y1];
}
void chkmax(short &a, short b) {a<b?a=b:;}
int main() {
scanf("%hd%hd", &h, &w);
int x, y;
REP(i,,h) {
scanf("%s",s[i]+);
REP(j,,w) a[i][j]=a[i-][j]+a[i][j-]-a[i-][j-]+(s[i][j]=='o');
REP(j,,w) if (s[i][j]=='E') x = i, y = j;
}
short ans = ;
PER(a,,x) REP(c,x,h) PER(b,,y) REP(d,y,w) {
int x1=-x+c,x2=h-x+a;
int y1=-y+d,y2=w-y+b;
short r = dp[b][c][d];
if (a>) {
chkmax(dp[b][c][d],r+C(max(x1,a-),max(y1,b),min(x2,a-),min(y2,d)));
}
if (c<h) {
chkmax(dp[b][c+][d],r+C(max(x1,c+),max(y1,b),min(x2,c+),min(y2,d)));
}
if (b>) {
chkmax(dp[b-][c][d],r+C(max(x1,a),max(y1,b-),min(x2,c),min(y2,b-)));
}
if (d<w) {
chkmax(dp[b][c][d+],r+C(max(x1,a),max(y1,d+),min(x2,c),min(y2,d+)));
}
chkmax(ans,r);
}
printf("%hd\n", ans);
}

24. 3734 Christmas Tree

大意: 要求用$A$条长度不超过$B$的链合并为一颗给定树, 求$(A,B)$字典序最小值

先考虑$A$的最小值, 假设奇数度数的点有$O$个, 那么显然$O$是偶数, 并且$A$的最小值就为$\frac{O}{2}$. 这是因为每次在一棵树上接上一条链, 最多产生两个奇数度数的点. 所以最小值就是$\frac{O}{2}$, 每次都选两个奇数度数点作为路径端点就可以达到$\frac{O}{2}$. 然后求$B$最小值, 问题可以转化为给定$O$个特殊点, 要求两两组合的路径长度最大值的最小值, 可以二分答案然后树形dp求出. $dp$时要选一个奇数度数的点作为根, 保证根节点儿子数为奇数.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n;
vector<int> g[N], h[N]; int dfs(int x, int f, int d) {
h[x].clear();
for (int y:g[x]) if (y!=f) h[x].pb(dfs(y,x,d));
if (h[x].size()%==) h[x].pb();
if (h[x].size()==) return h[x][]+;
sort(h[x].begin(),h[x].end());
int sz=h[x].size(),l=,r=sz-,ans=-;
while (l<=r) {
int ql=,qr=sz-,ok=;
while () {
if (ql==mid) ++ql;
if (qr==mid) --qr;
if (ql>qr) break;
if (h[x][ql]+h[x][qr]>d) ok=;
++ql,--qr;
}
if (ok) ans=mid,r=mid-;
else l=mid+;
}
return ans<?INF:h[x][ans]+;
} int main() {
scanf("%d", &n);
REP(i,,n) {
int u, v;
scanf("%d%d", &u, &v);
g[u].pb(v),g[v].pb(u);
}
int cnt = , rt = ;
REP(i,,n) if (g[i].size()&) rt = i, ++cnt;
cnt /= ;
int l = , r = n-, ans;
while (l<=r) {
if (dfs(rt,,mid)<=mid+) ans=mid,r=mid-;
else l=mid+;
}
printf("%d %d\n", cnt, ans);
}

25. 2689 Prime Flip

大意: 给定$01$序列, 每次操作选一个长度为奇素数的区间翻转, 求最少多少次使序列全零.

奇素数区间花费为$1$, 偶数长的区间花费为$2$, 否则花费为$3$

可以先差分一下, 转化为每次选两个相差为奇素数的点改变颜色, 可以先跑出最大匹配, 尽量匹配奇素数区间, 然后匹配偶数区间, 最后若有剩余则花费为3

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii; const int N = 1e6+, S = N-, T = N-, INF = 0x3f3f3f3f;
const int M = 1e7+;
int n, m, f[M];
struct edge {
int to,w,next;
edge(int to=,int w=,int next=):to(to),w(w),next(next){}
} e[N];
int head[N], dep[N], vis[N], cur[N], cnt=;
queue<int> Q;
int bfs() {
REP(i,,m) dep[i]=INF,vis[i]=,cur[i]=head[i];
dep[S]=INF,vis[S]=,cur[S]=head[S];
dep[T]=INF,vis[T]=,cur[T]=head[T];
dep[S]=,Q.push(S);
while (Q.size()) {
int u = Q.front(); Q.pop();
for (int i=head[u]; i; i=e[i].next) {
if (dep[e[i].to]>dep[u]+&&e[i].w) {
dep[e[i].to]=dep[u]+;
Q.push(e[i].to);
}
}
}
return dep[T]!=INF;
}
int dfs(int x, int w) {
if (x==T) return w;
int used = ;
for (int i=cur[x]; i; i=e[i].next) {
cur[x] = i;
if (dep[e[i].to]==dep[x]+&&e[i].w) {
int f = dfs(e[i].to,min(w-used,e[i].w));
if (f) used+=f,e[i].w-=f,e[i^].w+=f;
if (used==w) break;
}
}
return used;
}
int dinic() {
int ans = ;
while (bfs()) ans+=dfs(S,INF);
return ans;
}
void add(int u, int v, int w) {
e[++cnt] = edge(v,w,head[u]);
head[u] = cnt;
e[++cnt] = edge(u,,head[v]);
head[v] = cnt;
} int chk(int x) {
if (x<=) return ;
int mx = sqrt(x+0.5);
REP(i,,mx) if (x%i==) return ;
return ;
}
int main() {
scanf("%d", &n);
REP(i,,n) {
int t;
scanf("%d", &t);
f[t] = ;
}
vector<pii> A, B;
REP(i,,M-) if (f[i]!=f[i-]) {
if (i&) A.pb(pii(i,++m)),add(S,m,);
else B.pb(pii(i,++m)),add(m,T,);
}
for (auto t:A) for (auto tt:B) {
if (chk(abs(t.x-tt.x))) {
add(t.y,tt.y,);
}
}
int flow = dinic();
int ans = flow, x = A.size()-flow, y = B.size()-flow;
ans += x/*, x %= ;
ans += y/*, y %= ;
if (x) ans += ;
printf("%d\n", ans);
}

26. 2698 Don't Be a Subsequence

大意: 给定一个串$s$, 求最短的串$t$, 使得$t$不是$s$的子串, 多种方案时输出字典序最小的.

对于一个串的答案可以贪心, 每当出现'a'-'z'的所有字母后划分一个区间, 最短长度就为区间数+1. 所以可以预处理出每个后缀的答案, 然后序列自动机贪心即可

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
char s[N], ans[N];
int n, f[N], nxt[N][]; int main() {
scanf("%s",s+);
n = strlen(s+);
REP(i,,) nxt[n+][i]=nxt[n+][i]=n+;
int sta = , mx = (<<)-;
f[n+] = ;
PER(i,,n) {
sta |= <<s[i]-'a';
f[i] = f[i+];
if (sta==mx) ++f[i], sta = ;
memcpy(nxt[i],nxt[i+],sizeof nxt[]);
nxt[i][s[i]-'a']=i;
}
f[n+] = ;
int len = f[], now = ;
REP(i,,len) {
REP(j,'a','z') {
int t = nxt[now][j-'a']+;
if (i+f[t]<=len) {
now = t, ans[i] = j;
break;
}
}
}
puts(ans+);
}

27. 2699 Flip and Rectangles

大意: 给定棋盘, 每次操作翻转一行或一列, 求最大全$1$矩形面积

可以发现一个矩形如果能全变为$1$, 那么等价于任意一个$2\times 2$的矩形$1$的个数为偶数. 所以就转化为简单的最大全$1$矩形问题, 要特判一下长或宽为$1$的矩形

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 2e3+;
int n, m, a[N][N], L[N][N], R[N][N], H[N][N];
char s[N][N]; int main() {
scanf("%d%d", &n, &m);
REP(i,,n) scanf("%s", s[i]+);
REP(i,,n-) REP(j,,m-) {
int t = (s[i][j]=='#')^(s[i+][j]=='#')^(s[i][j+]=='#')^(s[i+][j+]=='#');
a[i][j] = !t;
}
REP(i,,n-) {
REP(j,,m-) if (a[i][j]) {
if (a[i][j-]) L[i][j] = L[i][j-];
else L[i][j] = j;
}
PER(j,,m-) if (a[i][j]) {
if (a[i][j+]) R[i][j] = R[i][j+];
else R[i][j] = j;
}
}
int ans = max(n,m);
REP(i,,n-) REP(j,,m-) if (a[i][j]) {
H[i][j] = H[i-][j]+;
if (H[i-][j]) {
L[i][j] = max(L[i][j],L[i-][j]);
R[i][j] = min(R[i][j],R[i-][j]);
}
ans = max(ans, (H[i][j]+)*(R[i][j]-L[i][j]+));
}
printf("%d\n", ans);
}

28. 2363 Tautonym Puzzle

大意: 若一个序列满足前一半与后一半相同, 则为好序列. 求构造一个序列使得非空好子序列个数为$N$

构造题. 一个想法是连续$x$个数贡献是$2^x-1$, 然后直接划分成若干个连续相同的段, 但是这样长度会超限.

一个倍增的做法是, 对于每种数字只用两次, 第一次全在串$A$内, 第二次全在串$B$内, 并且考虑空子序列的情况.

假设$AB$的答案为$x$, 那么新添一个数字$t$, $tAtB$答案为$2x$, $tABt$答案为$x+1$, 直接递归即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head #ifdef ONLINE_JUDGE
const int N = 1e6+;
#else
const int N = 1e2+;
#endif deque<int> A, B;
int now; void solve(ll n) {
if (n==) return;
solve(n>>);
++now;
A.push_front(now);
B.push_front(now);
if (n&) {
++now;
A.push_front(now);
B.push_back(now);
}
} int main() {
ll n;
cin>>n;
solve(n+);
cout<<A.size()+B.size()<<endl;
for (auto t:A) cout<<t<<' ';
for (auto t:B) cout<<t<<' ';
cout<<endl;
}

29. 2366 Prefix Median

大意: 给定长$2n-1$的序列$a$, 求所有$a$的排列中, 能生成多少种序列$b$. 其中$b_i$为$a$中前$2i-1$个数的中位数.

好难的$dp$题, 参考了好多篇题解才大概懂, 不愧是AGC的F题

先把$a$排序, 可以注意到$b$必须满足三个条件

  • $b_{i}$是$\{ a_i,a_{i+1},...,a_{2n-i}\}$中的一个数
  • 不存在$i<j$, 满足$b_j<b_i<b_{j+1}$
  • 不存在$i<j$, 满足$b_{j}>b_{i}>b_{j+1}$

下面证明这三个条件是充分的, 也就是说证明所有满足三个条件的序列$b$都可以构造出对应的$a$.

只考虑$a$是排列的情况, 有重复元素的话证明类似

显然$b_n=n, b_{n-1}$为$n-1,n,n+1$其中之一

  • 若$b_{n-1}=n-1$, 那么从$\{n,...,2n-1\}$中取出最小的两个未在$b_1,...,b_{n-1}$中出现过的数, 填到$a_{2n-2},a_{2n-1}$中
  • 若$b_{n-1}=n+1$, 那么从$\{1,...,n\}$中取出最大的两个未在$b_1,...,b_{n-1}$中出现过的数, 填到$a_{2n-2},a_{2n-1}$中
  • 若$b_{n-1}=n$, 那么从$\{1,...,n-1\}$中取最大未出现的数, $\{n+1,...,2n-1\}$中取最小, 填到$a_{2n-2},a_{2n-1}$中

这样取完后, $b_{n-1}$一定为剩余数的中位数, 由于三个条件的限制, 那么$b_{n-2}$一定是剩余数中中间的三个之一, 所以递归下去即可构造出$a$.

然后就是简单$dp$了, 设$dp_{i,j,k}$为已经填完$b_{i+1},...,b_{n}$, 现在要填$b_{i}$, 比$b_{i+1}$小的可选数有$j$个, 比$b_{i+1}$大的可选数有$k$个的方案数. 枚举$b_{i}$的选取情况转移即可, 前缀和优化一下可以达到复杂度$O(n^3)$, 直接暴力转移$O(n^4)$也能过.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = ;
int n, a[N], dp[][N][N]; void add(int &a, int b) {a+=b;if (a>=P) a-=P;} int main() {
scanf("%d", &n);
int m = *n-;
REP(i,,m) scanf("%d", a+i);
sort(a+,a++m);
int cur = ;
dp[][][] = ;
PER(i,,n-) {
cur ^= ;
memset(dp[cur],,sizeof dp[]);
int l = a[i]!=a[i+], r = a[*n-i-]!=a[*n-i];
REP(j,,m) REP(k,,m) {
int &ret = dp[!cur][j][k];
if (!ret) continue;
//b[i]=b[i+1]
add(dp[cur][j+l][k+r],ret);
//b[i]<b[i+1]
REP(t,,j+l-) add(dp[cur][t][k+r+],ret);
//b[i]>b[i+1]
REP(t,,k+r-) add(dp[cur][j+l+][t],ret);
}
}
int ans = ;
REP(i,,m) REP(j,,m) add(ans,dp[cur][i][j]);
printf("%d\n", ans);
}

O(n^4)

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = ;
int n, a[N], dp[][N][N];
int sum1[N][N], sum2[N][N];
void add(int &a, int b) {a+=b;if (a>=P) a-=P;} int main() {
scanf("%d", &n);
int m = *n-;
REP(i,,m) scanf("%d", a+i);
sort(a+,a++m);
dp[][][] = ;
int cur = , ans = ;
PER(i,,n-) {
REP(j,,m) PER(k,,m) {
add(sum1[k-][j],sum1[k][j]);
add(sum2[j][k-],sum2[j][k]);
}
REP(j,,m) REP(k,,m) {
add(dp[cur][j][k],sum1[j][k]),sum1[j][k]=;
add(dp[cur][j][k],sum2[j][k]),sum2[j][k]=;
dp[!cur][j][k] = ;
if (!i) add(ans,dp[cur][j][k]);
}
if (!i) break;
cur ^= ;
int l = a[i]!=a[i+], r = a[*n-i-]!=a[*n-i];
REP(j,,m) REP(k,,m) {
int &ret = dp[!cur][j][k];
if (!ret) continue;
//b[i]=b[i+1]
add(dp[cur][j+l][k+r],ret);
//b[i]<b[i+1]
if (j+l->=) add(sum1[j+l-][k+r+],ret);
//b[i]>b[i+1]
if (k+r->=) add(sum2[j+l+][k+r-],ret);
}
}
printf("%d\n", ans);
}

O(n^3)

30. 2703 Shift and Flip

大意: 给定两个01串$A,B$, 每次操作把$A$循环左移或右移, 或者选一个$B_i=1$的$i$, 翻转$A_i$, 求最少操作次数使得$A,B$相同

假设有一个计数器初始为$0$, 左移$-1$,右移$+1$, 最小值为$L$,最大值为$R$,最终值为$d$.

可以发现如果$d$固定, 那么$A$中每个位置的数是否需要翻转就已经固定, 并且可以知道最优情况要么是先左移到$L$,右移到$R$,再移到$d$. 要么是先右移到$R$,左移到$L$,再移到$d$.

然后考虑翻转操作, $B$中位置$i$的$1$可以翻转的区间就为$[i+L,i+R]$, 只要所有的$1$对应区间覆盖到所有$A$中需要翻转的位置, 那么就合法, 翻转的花费就为$A$中需要翻转的位数.

所以可以$O(n^2)$枚举$L,d$, 求出最小合法的右移距离$R$即可.

具体实现的话, 注意到$L$递增的时候, $R$是单调递减的, 可以直接用可撤销链表模拟.

感觉这个题还是比较考验码力的, 花了两个小时才写完, 主要还是我可撤销链表和双指针不熟练, 以后要找时间再打一遍这个题

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e4+;
int n, vis[N];
char a[N],b[N],c[N];
struct _ {
int p,l,r;
} L[N],R[N];
vector<pair<_*,_> > tag[N];
vector<int> tag2[N]; int solve(int d) {
REP(i,,n) tag[i].clear(),tag2[i].clear();
int ans = , cnt = ;
REP(i,,n) {
vis[i] = ;
int nxt = i-d;
if (nxt<=) nxt += n;
c[nxt] = b[i]^a[i];
if (b[i]) L[++cnt].p = i;
}
REP(i,,n) ans += c[i];
REP(i,,cnt) {
L[i].l=i-,L[i].r=i+,R[i]=L[i];
}
L[].r = , R[] = L[];
if (!ans) return d;
int tot = , pl = , pr = ;
REP(i,,n) if (b[i]) {
vis[i] = ;
if (c[i]) ++tot;
}
if (tot==ans) return ans+d;
auto add = [&](_ f[], int &p, int tp) {
p += tp;
for (int i=f[].r; i!=cnt+; i=f[i].r) {
int nxt = f[i].p-tp;
if (nxt==) nxt=n;
if (nxt>n) nxt=;
f[i].p = nxt;
if (tp<) {
if (vis[nxt]) {
tag[abs(p)].pb(pair<_*,_>(&f[f[i].l],f[f[i].l]));
tag[abs(p)].pb(pair<_*,_>(&f[f[i].r],f[f[i].r]));
f[f[i].l].r=f[i].r;
f[f[i].r].l=f[i].l;
}
else {
tag2[abs(p)].pb(nxt);
vis[nxt] = ;
if (c[nxt]) ++tot;
}
}
else {
if (vis[nxt]>=) f[f[i].l].r=f[i].r,f[f[i].r].l=f[i].l;
else {
if (!vis[nxt]&&c[nxt]) ++tot;
vis[nxt] += ;
}
}
}
};
while (tot<ans) add(L,pl,-);
int ret = min(abs(pl)+abs(pr-pl)+abs(d-pr),abs(pr)+abs(pl-pr)+abs(d-pl));
do {
for (auto &t:tag[abs(pl)]) *(t.x) = t.y;
for (auto &t:tag2[abs(pl)]) {
--vis[t];
if (!vis[t]&&c[t]) --tot;
}
++pl;
while (tot<ans) add(R,pr,);
ret = min(ret, min(abs(pl)+abs(pr-pl)+abs(d-pr),abs(pr)+abs(pl-pr)+abs(d-pl)));
} while (pl);
return ans+ret;
} int solve() {
int ans = 1e9;
REP(d,,n-) {
ans = min(ans, solve(d));
char t = a[n];
PER(i,,n) a[i]=a[i-];
a[] = t;
}
return ans;
} char tmp[N]; int main() {
scanf("%s%s",a+,b+);
n = strlen(a+);
if (strcmp(a+,b+)==) return puts(""),;
if (*max_element(b+,b++n)=='') return puts("-1"),;
REP(i,,n) a[i]-='',b[i]-='',tmp[i]=a[i];
int ans = solve();
REP(i,,n) a[i]=tmp[i];
reverse(a+,a++n);
reverse(b+,b++n);
ans = min(ans, solve());
printf("%d\n", ans);
}

31. 2396 Infinite Sequence

大意: 定义一个由$\{1,...,n\}$组成的无限长序列, 满足(1)第$n$项及以后的项全相等(2)第$i$项之后的$a_i$项相等, 求所有不同的序列个数

设${dp}_i$为$i...n$的填数方案, 那么有${dp}_n=n,{dp}_{n-1}=n^2$

对于$i\le n-2$, 若填$1$, 方案数为${dp}_{i+1}$

若连续填两个$>1$的数, 那么之后全部相同, 方案数为$(n-1)^2$

若填一个$>1$的数$x$, 后面接$x$个$1$, 那么总方案数为$\sum\limits_{j=i+3}^n f[j]$, 再加上填的$1$超出$n$的部分$i+1$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
int n, f[N]; int main() {
scanf("%d", &n);
f[n] = n, f[n-] = (ll)n*n%P;
int s = ;
PER(i,,n-) {
s = (s+f[i+])%P;
f[i] = (s+i++(ll)(n-)*(n-)+f[i+])%P;
}
printf("%d\n", f[]);
}

32. 2401 Alice in linear land

大意: 有一条直线, $Alice$初始在$0$, 要去位置$D$. 给定一个序列$d$, 第$i$步若向终点走$d_i$能缩短到终点的距离就走$d_i$, 否则停在原地不动. 给定$Q$个询问$q_i$, 若修改$d_{q_i}$的值后, 能使$Alice$不能到达位置$D$输出YES, 否则输出NO

假设$a_i$表示前$i$次操作后的距终点距离, 那么修改第$q$次操作后, 距离范围就为$[0,a_{q-1}]$

设$b_i$为最小数, 满足前$i$步后若在$b_i$则不能达到终点, 那么显然只要$b_q\le a_{q-1}$答案就为YES, 可以得到

  • 显然$b_n=1$
  • 若$b_{i+1}\le \lfloor\frac{d_{i+1}}{2}\rfloor, b_{i}=b_{i+1}$
  • 否则, $b_{i}=b_{i+1}+d_{i+1}$
#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<' ';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 1e6+;
ll d[N],a[N];
ll b[N]; int main() {
int n=rd(),D=rd();
REP(i,,n) d[i]=rd();
a[] = D;
REP(i,,n) a[i] = min(a[i-],abs(a[i-]-d[i]));
b[n] = ;
PER(i,,n-) {
if (b[i+]<=d[i+]/) b[i]=b[i+];
else b[i]=b[i+]+d[i+];
}
int q=rd();
REP(i,,q) {
int x = rd();
puts(b[x]<=a[x-]?"YES":"NO");
}
}

33. 2062 ~K Perm Counting

大意: 给定$n,k$, 求不存在$|p_i-i|=k$的$n$排列个数.

假设有一个二分图, $L$部为序列下标, $R$部为值, 那么排列$p$对应了一个完美匹配.

考虑一个只含边$(L_i,R_{i+k}),(L_i,R_{i-k})$的二分图

假设它大小为$k$的匹配数为$M_k$, 根据容斥得到答案就为$\sum\limits_{i=0}^n (-1)^i M_i (n-i)!$

$M_k$可以很容易用$O(n^2)$的$dp$求出.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = , INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 2e3+;
int n,k,vis[*N],dp[*N][N][],fac[N];
void add(int &a, int b) {a+=b;if(a>=P)a-=P;} int main() {
fac[]=;
REP(i,,N-) fac[i]=(ll)fac[i-]*i%P;
scanf("%d%d",&n,&k);
int cnt = ;
//分别对2k条链进行dp,每条边挂在下一个点上,链头没有边
REP(i,,k) {
for(int j=i;j<=n;j+=k) vis[++cnt]=i!=j;
for(int j=i;j<=n;j+=k) vis[++cnt]=i!=j;
}
dp[][][] = ;
REP(i,,*n) REP(j,,min(n,i)) {
add(dp[i][j][],dp[i-][j][]);
add(dp[i][j][],dp[i-][j][]);
if (vis[i]) add(dp[i][j+][],dp[i-][j][]);
}
int ans = ;
REP(i,,n) {
int t = (dp[*n][i][]+dp[*n][i][])%P;
if (i&) ans=(ans-(ll)t*fac[n-i])%P;
else ans=(ans+(ll)t*fac[n-i])%P;
}
if (ans<) ans += P;
printf("%d\n", ans);
}

O(n^2)

实际上可以发现对于一条长$x$的链, 匹配数为$k$就相当于取$k$条不相邻的边

那么方案数就为$\binom{x-k+1}{k}$, 用$NTT$把所有链贡献乘一下即可, 复杂度$O(nlogn)$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
const int N = 1e4+, P = , G = , Gi = ;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;} int lim,l,A[N],B[N],R[N];
void init(int n) {
for (lim=,l=; lim<=n; lim<<=,++l) ;
REP(i,,lim-) R[i]=(R[i>>]>>)|((i&)<<(l-));
} void NTT(int *J, int tp=) {
REP(i,,lim-) if (i<R[i]) swap(J[i],J[R[i]]);
for (int j=; j<lim; j<<=) {
ll T = qpow(tp==?G:Gi,(P-)/(j<<));
for (int k=; k<lim; k+=j<<) {
ll t = ;
for (int l=; l<j; ++l,t=t*T%P) {
int y = t*J[k+j+l]%P;
J[k+j+l] = (J[k+l]-y)%P;
J[k+l] = (J[k+l]+y)%P;
}
}
}
if (tp==-) {
ll inv = qpow(lim, P-);
REP(i,,lim-) J[i]=(ll)inv*J[i]%P;
}
} int n,k,fac[N],ifac[N],a[N],b[N];
int C(int n, int m) {
if (m>n) return ;
return (ll)fac[n]*ifac[m]%P*ifac[n-m]%P;
} int main() {
fac[] = ;
REP(i,,N-) fac[i] = (ll)fac[i-]*i%P;
ifac[N-] = inv(fac[N-]);
PER(i,,N-) ifac[i]=(ll)ifac[i+]*(i+)%P;
scanf("%d%d", &n, &k);
int x=n/k,y=n/k+;
REP(i,,x) a[i]=C(x-i,i);
REP(i,,y) b[i]=C(y-i,i);
init(*n+);
NTT(a),NTT(b);
REP(i,,lim-) a[i]=qpow(a[i],(k-n%k)*)*qpow(b[i],n%k*)%P;
NTT(a,-);
int ans = ;
REP(i,,n) {
if (i&) ans=(ans-(ll)a[i]*fac[n-i])%P;
else ans=(ans+(ll)a[i]*fac[n-i])%P;
}
if (ans<) ans += P;
printf("%d\n", ans);
}

O(nlogn)

34. 4511 Tree Burning

大意: 长$L$的环, 初始在$0$, 有$n$棵树, 每次顺时针或逆时针走, 烧掉第一棵为被烧的树, 求烧完所有树的最大距离和.

这个agc的$B$题竟然不会, 看了题解才懂

可以发现最优解一定形如LLLRLRLRL, 或者RRRLRLRLR

枚举转折点, 可以发现终点贡献是终点到起点的距离, 其余点的贡献是它到起点距离的2倍.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head #ifdef ONLINE_JUDGE
const int N = 1e6+;
#else
const int N = 1e2+;
#endif int L, n, a[N], d[N];
ll ans;
void solve() {
ll tot = ;
PER(i,,n) {
//倒序枚举, 逐步添点
d[i] = a[i], tot += d[i];
int len = n-i+, p;
//p为转折点
if (len&) p = n-len/;
else {
p = n-len/+;
tot -= d[p];
d[p] = L-d[p];
tot += d[p];
}
ans = max(ans, tot*-d[p]);
}
} int main() {
scanf("%d%d",&L,&n);
REP(i,,n) scanf("%d",a+i);
solve();
exit();
REP(i,,n) a[i] = L-a[i];
reverse(a+,a++n);
solve();
printf("%lld\n", ans);
}

35. 4512 Coloring Torus

大意: 给定$k$种颜色, 求构造一个$n\times n$的棋盘, 要求每个格子都涂上一种颜色, 每种颜色都至少用一次. 对于同种颜色的格子, 要求相邻格每种颜色的出现次数相同. 相邻指的是循环相邻.

注意到$a_{i,j}=(i+j)\mod n$时一定合法. 若$n$为偶数, 把奇数列全加上$n$仍然合法, 把$n+i$换成$i$仍然合法. 所以就可以构造出$[n,2n]$的所有$k$.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 2e3+;
int n,k,a[N][N]; int main() {
scanf("%d",&k);
if (k==) return puts("1\n1"),;
n = (k+)/*;
REP(i,,n) REP(j,,n) a[i][j]=(i+j)%n+i%*n;
REP(i,,n) REP(j,,n) if (a[i][j]>=k) a[i][j]-=n;
printf("%d\n", n);
REP(i,,n) {
REP(j,,n) printf("%d ", a[i][j]+);hr;
}
}

36. 4513 Inversion Sum

大意: 给定$n$元素序列$a$, $q$个操作$(x,y)$表示交换$a_x,a_y$. 按顺序执行$q$次操作, 可以选择跳过操作. 对于$2^q$种情况, 求出逆序对的和.

$O(n^3)$的$dp$很容易, 只需要设$dp_{i,j,k}$为前$i$次操作$a_j>a_k$的方案数, 暴力转移即可.

实际上每次转移影响到的状态会很少, 只有$O(n)$, 可以把方案数全除以$2$, 转成概率$dp$来做.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = ;
int n,q,a[N],x[N],y[N];
int dp[N][N][N]; int main() {
scanf("%d%d",&n,&q);
REP(i,,n) scanf("%d",a+i);
REP(i,,q) {
scanf("%d%d",x+i,y+i);
}
REP(i,,n) REP(j,,n) dp[][i][j] = a[i]>a[j];
REP(z,,q) {
REP(i,,n) REP(j,,n) {
if (x[z]==i) {
if (y[z]==j) {
dp[z][i][j] = dp[z-][i][j]+dp[z-][j][i];
}
else {
dp[z][i][j] = dp[z-][i][j]+dp[z-][y[z]][j];
}
}
else if (x[z]==j) {
if (y[z]==i) {
dp[z][i][j] = dp[z-][i][j]+dp[z-][j][i];
}
else {
dp[z][i][j] = dp[z-][i][j]+dp[z-][i][z[y]];
}
}
else {
if (y[z]==i) {
dp[z][i][j] = dp[z-][i][j]+dp[z-][x[z]][j];
}
else if (y[z]==j) {
dp[z][i][j] = dp[z-][i][j]+dp[z-][i][x[z]];
}
else {
dp[z][i][j] = dp[z-][i][j]*;
}
}
}
}
int ans = ;
REP(i,,n) REP(j,i+,n) ans = (ans+dp[q][i][j])%P;
printf("%d\n", ans);
}

O(n^3)

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const ll inv2 = (P+)/;
const int N = ;
int n,q,a[N],x[N],y[N];
int dp[N][N];
struct {int x,y;ll v;} s[N*]; int main() {
scanf("%d%d",&n,&q);
REP(i,,n) scanf("%d",a+i);
REP(i,,q) scanf("%d%d",x+i,y+i);
REP(i,,n) REP(j,,n) dp[i][j] = a[i]>a[j];
REP(z,,q) {
int top = ;
REP(i,,n) {
if (i!=y[z]) {
s[++top] = {x[z],i,(dp[x[z]][i]+dp[y[z]][i])*inv2%P};
s[++top] = {i,x[z],(dp[i][x[z]]+dp[i][y[z]])*inv2%P};
}
else {
s[++top] = {x[z],y[z],(dp[x[z]][y[z]]+dp[y[z]][x[z]])*inv2%P};
}
if (i!=x[z]) {
s[++top] = {y[z],i,(dp[y[z]][i]+dp[x[z]][i])*inv2%P};
s[++top] = {i,y[z],(dp[i][y[z]]+dp[i][x[z]])*inv2%P};
}
else {
s[++top] = {y[z],x[z],(dp[x[z]][y[z]]+dp[y[z]][x[z]])*inv2%P};
}
}
REP(i,,top) dp[s[i].x][s[i].y]=s[i].v;
}
int ans = ;
REP(i,,n) REP(j,i+,n) ans = (ans+dp[i][j])%P;
ans = ans*qpow(,q)%P;
printf("%d\n", ans);
}

O(n^2)

37. 3971 rng_10s

大意: 初始商店$A$瓶饮料, 一个人每天早上来买$B$瓶, 晚上若商店饮料数不超过$C$那么老板补充$D$瓶饮料, 求这个人是否每天都能买到$B$瓶.

$B<A$或$B<D$一定不成立,$B\le C+1$一定成立, 那么就转化为在模$B$剩余系内, 初始值为$A$, 每次增加$D$, 判断是否会到达$[C+1,B-1]$的点, 就等价于判断$[C+1,B-1]$内的点与$A$模$gcd(D,B)$同余

38. 2003 BBuBBBlesort!

大意: 给定序列$a$, 元素各不相同, 每次操作翻转连续两位或三位, 求翻转两位的最少次数, 使序列升序排列.

题目保证没有重复元素, 那就等价于给定一个排列. 翻转三位相当于是交换间隔2的元素, 那么每次交换不改变与最终位置的奇偶性

39. 4169 Colorful Sequences

大意: 若一个序列存在一段$k$的子串为$1...k$的排列, 那么这个序列为合法序列. 给定$n,m,k$,给定长$m$的序列$A$, 求$A$在所有长$n$的合法序列中的出现次数和.

$A$在所有序列中的出现次数为$(n-m+1)k^{n-m}$, 转化为求$A$在不合法序列中的出现次数和.

(1)若$A$已经合法, 那么答案为$0$.

(2)若$A$中元素互不相同, 那么可以$dp$求出长$m$的所有互不相同的序列在所有不合法序列中的方案, 最后再除以$\frac{k!}{(k-m)!}$.

设状态$(i,j)$表示长$i$,最后一段连续互不相同的段长$j$, 那么转移图很容易画出来.

定义所有$m\le j\le k-1$的点的权值为$1$, 否则为$0$, 那么答案就为从$(0,0)$到$(n,1...k-1)$所有路径的点权和.

记$f_{i,j}$为$(0,0)$到$(i,j)$的路径数, $g_{i,j}$为$(0,0)$到$(i,j)$的所有路径的点权和, 转移可以后缀优化在$O(nk)$的时间求出.

(3)若$A$中元素有相同的, 那么存在一个互不相同的前缀和后缀, 两部分是独立的, 并且总的方案只与这两部分有关. 可以枚举$A$在所有不合法序列中的出现位置, $dp$算出对应不合法序列数, $dp$过程和(2)类似.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=%P;for (a%=P;n;a=a*a%P,n>>=)if(n&)r=r*a%P;return r;}
ll inv(ll x){return x<=?:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=;char p=getchar();while(p<''||p>'')p=getchar();while(p>=''&&p<='')x=x*+p-'',p=getchar();return x;}
//head const int N = 2.6e4+, M = ;
int n, k, m, a[N];
int f[N][M],g[N][M];
int vis[N],fac[N],ifac[N];
void add(int &a, ll b) {a=(a+b)%P;} int main() {
fac[] = ;
REP(i,,N-) fac[i]=(ll)fac[i-]*i%P;
ifac[N-]=inv(fac[N-]);
PER(i,,N-) ifac[i]=(ll)ifac[i+]*(i+)%P;
scanf("%d%d%d", &n, &k, &m);
REP(i,,m) scanf("%d",a+i);
int ma = , now = ;
REP(i,,m) {
while (now<m&&!vis[a[now+]]) vis[a[++now]]=;
ma = max(ma, now-i+);
--vis[a[i]];
}
int tot = (n-m+1ll)*qpow(k,n-m)%P;
if (ma==k) return printf("%d\n",tot),;
if (ma==m) {
f[][] = ;
REP(i,,n) {
int s1 = , s2 = ;
PER(j,,min(k-,i)) {
add(s1,f[i-][j]),add(s2,g[i-][j]);
add(f[i][j]=s1,(ll)(k-j+)*f[i-][j-]);
add(g[i][j]=s2,(ll)(k-j+)*g[i-][j-]);
if (j>=m) add(g[i][j],f[i][j]);
}
}
int ans = ;
REP(i,,k-) (ans += g[n][i])%=P;
ans = (ll)ans*ifac[k]%P*fac[k-m]%P;
ans = (tot-ans+P)%P;
printf("%d\n", ans);
}
else {
int L = , R = ;
memset(vis,,sizeof vis);
while (!vis[a[L+]]) vis[a[++L]]=;
memset(vis,,sizeof vis);
while (!vis[a[m-R]]) vis[a[m-R++]]=;
f[][L] = g[][R] = ;
REP(i,,n) {
int s1 = , s2 = ;
PER(j,,k-) {
add(s1,f[i-][j]),add(s2,g[i-][j]);
add(f[i][j]=s1,(ll)(k-j+)*f[i-][j-]);
add(g[i][j]=s2,(ll)(k-j+)*g[i-][j-]);
}
}
int ans = ;
REP(i,,n-m) {
int s1 = , s2 = ;
REP(j,,k-) add(s1,f[i][j]),add(s2,g[n-m-i][j]);
add(ans,(ll)s1*s2);
}
ans = (tot-ans+P)%P;
printf("%d\n", ans);
}
}

AtCoder练习的更多相关文章

  1. AtCoder Regular Contest 061

    AtCoder Regular Contest 061 C.Many Formulas 题意 给长度不超过\(10\)且由\(0\)到\(9\)数字组成的串S. 可以在两数字间放\(+\)号. 求所有 ...

  2. AtCoder Grand Contest 001 C Shorten Diameter 树的直径知识

    链接:http://agc001.contest.atcoder.jp/tasks/agc001_c 题解(官方): We use the following well-known fact abou ...

  3. AtCoder Regular Contest 082

    我都出了F了……结果并没有出E……atcoder让我差4分上橙是啥意思啊…… C - Together 题意:把每个数加1或减1或不变求最大众数. #include<cstdio> #in ...

  4. AtCoder Regular Contest 069 D

    D - Menagerie Time limit : 2sec / Memory limit : 256MB Score : 500 points Problem Statement Snuke, w ...

  5. AtCoder Regular Contest 076

    在湖蓝跟衡水大佬们打的第二场atcoder,不知不觉一星期都过去了. 任意门 C - Reconciled? 题意:n只猫,m只狗排队,猫与猫之间,狗与狗之间是不同的,同种动物不能相邻排,问有多少种方 ...

  6. AtCoder Grand Contest 016

    在雅礼和衡水的dalao们打了一场atcoder 然而窝好菜啊…… A - Shrinking 题意:定义一次操作为将长度为n的字符串变成长度n-1的字符串,且变化后第i个字母为变化前第i 或 i+1 ...

  7. AtCoder Beginner Contest 069【A,水,B,水,C,数学,D,暴力】

    A - K-City Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement In K-city, ...

  8. AtCoder Beginner Contest 075 D - Axis-Parallel Rectangle

    https://beta.atcoder.jp/contests/abc075/tasks/abc075_d 题意: 给出坐标平面上n个点的坐标,要求找到一个面积最小的矩形使得这个矩形的边界加上内部的 ...

  9. AtCoder Beginner Contest 073

    D - joisino's travel Time Limit: 2 sec / Memory Limit: 256 MB Score : 400400 points Problem Statemen ...

  10. AtCoder ABC 042D いろはちゃんとマス目 / Iroha and a Grid

    题目链接:https://abc042.contest.atcoder.jp/tasks/arc058_b 题目大意: 给定一个 H * W 的矩阵,其中左下角 A * B 区域是禁区,要求在不踏入禁 ...

随机推荐

  1. 分享CSS3里box-shadow属性的使用方法,包括内阴影box-shadow:inset

    一.box-shadow语法 box-shadow: none | inset(可选值,不设置,为外投影,设置,为内投影) x-offset(阴影水平偏移量,正方向为right) y-offset(阴 ...

  2. php-图片加水印和文字

    //************************** 图片加文字 ************************ $dst_path = '11.jpg';//创建图片的实例$dst = ima ...

  3. angular中的服务和持久化实现

    1.创建服务: ng g service my-new-service 创建到指定目录下面 ng g service services/storage 2.app.module.ts 里面引入创建的服 ...

  4. removeEntry

    void VCGantt::removeEntry() { QModelIndexList selectedIndexes = ganttViewCommon->selectionModel() ...

  5. osg qt fbx ifc bim osg ive 3ds max rvt

    项目环境变量配置 include E:\Qt\Qt5.12.2\5.12.2\msvc2017_64\include E:\OpenSourceGraph\OpenSceneGraph_install ...

  6. 19 Flutter 自定义AppBar 定义顶部Tab切换 底部Tab结合顶部Tab实现类似头条页面布局(27分36秒)

    Flutter AppBar自定义顶部导航按钮图标.颜色以及TabBar定义顶部Tab切换. leading:在标题前面显示的一个控件,在首页通常显示应用的logo:在其他界面通常显示为付汇按钮. t ...

  7. 阶段5 3.微服务项目【学成在线】_day09 课程预览 Eureka Feign_13-课程预览功能开发-CMS页面预览接口测试

    5.2 CMS页面预览测试 CMS已经提供了页面预览功能,课程预览功能要使用CMS页面预览接口实现,下边通过cms页面预览接口测试课 程预览的效果. 1.向cms_page表插入一条页面记录或者从cm ...

  8. Qwidget::update

    void QWidget::update ()分析重绘事件激活 1看看手册中这段话 void QWidget::update () [slot] Updates the widget unless u ...

  9. PAT 甲级 1044 Shopping in Mars (25 分)(滑动窗口,尺取法,也可二分)

    1044 Shopping in Mars (25 分)   Shopping in Mars is quite a different experience. The Mars people pay ...

  10. Delphi下Treeview控件基于节点编号的访问

    有时我们需要保存和重建treeview控件,本文提供一种方法,通过以树结构节点的编号访问树结构,该控件主要提供的方法如下:      function GetGlobeNumCode(inNode:T ...