6592 Beauty Of Unimodal Sequence
线段树优化dp
f0[i]代表必须下降,f1[i]代表后缀下降剩下的前缀上升
同时维护f数组最优情况下的最靠左最靠右转移点
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
typedef long long s64;
const int M=3e5+5;
int n;
int a[M];
int f0[M],f1[M],L0[M],R0[M],L1[M],R1[M];
int Max[2][M*4],L[2][M*4],R[2][M*4];
int ans[3];
struct node {
int x,id;
bool operator <(const node &a) const {
return x<a.x;
}
}p[M];
void T_add(int g,int l,int r,int x,int y) {
if(l==r) {
if(f0[y]>Max[0][g]) {
Max[0][g]=f0[y];
L[0][g]=R[0][g]=y;
}
else if(f0[y]==Max[0][g]) {
L[0][g]=min(L[0][g],y);
R[0][g]=max(R[0][g],y);
}
if(f1[y]>Max[1][g]) {
Max[1][g]=f1[y];
L[1][g]=R[1][g]=y;
}
else if(f1[y]==Max[1][g]) {
L[1][g]=min(L[1][g],y);
R[1][g]=max(R[1][g],y);
}
return;
}
int mid=(l+r)>>1;
if(x<=mid) T_add(g<<1,l,mid,x,y);
else T_add(g<<1|1,mid+1,r,x,y);
int ls=g<<1,rs=g<<1|1;
if(Max[0][ls]<Max[0][rs]) swap(ls,rs);
Max[0][g]=Max[0][ls];
L[0][g]=L[0][ls],R[0][g]=R[0][ls];
if(Max[0][ls]==Max[0][rs]) {
L[0][g]=min(L[0][g],L[0][rs]);
R[0][g]=max(R[0][g],R[0][rs]);
}
if(Max[1][ls]<Max[1][rs]) swap(ls,rs);
Max[1][g]=Max[1][ls];
L[1][g]=L[1][ls],R[1][g]=R[1][ls];
if(Max[1][ls]==Max[1][rs]) {
L[1][g]=min(L[1][g],L[1][rs]);
R[1][g]=max(R[1][g],R[1][rs]);
}
}
void query(int g,int l,int r,int lx,int rx,int opt) {
//cout<<g<<" "<<l<<" "<<r<<endl;
if(lx>rx) return;
if(lx<=l&&rx>=r) {
if(Max[opt][g]>ans[0]) {
ans[0]=Max[opt][g];
ans[1]=L[opt][g];
ans[2]=R[opt][g];
}
else if(Max[opt][g]==ans[0]) {
ans[1]=min(ans[1],L[opt][g]);
ans[2]=max(ans[2],R[opt][g]);
}
return;
}
int mid=(l+r)>>1;
if(lx<=mid) query(g<<1,l,mid,lx,rx,opt);
if(rx>mid) query(g<<1|1,mid+1,r,lx,rx,opt);
}
void find_min() {
int da=0;
rep(i,1,n) da=max(da,f0[i]),da=max(da,f1[i]);
int pre;
rep(i,1,n) if(f0[i]==da||f1[i]==da) {pre=i;break;}
if(da==1) return printf("%d\n",pre),void();
printf("%d ",pre);
bool vis=0;
rep(i,2,da) {
if(f0[pre]==f1[pre]&&!vis) {
if(L1[pre]<L0[pre]) vis=1;
pre=min(L0[pre],L1[pre]);
}
else if(f1[pre]==da-i+2) {
pre=L1[pre];
vis=1;
}
else if(f0[pre]==da-i+2) {
pre=L0[pre];
}
if(i!=da) printf("%d ",pre);
else printf("%d\n",pre);
}
}
void find_max() {
int da=0;
rep(i,1,n) da=max(da,f0[i]),da=max(da,f1[i]);
int pre;
per(i,n,1) if(f0[i]==da||f1[i]==da) {pre=i;break;}
if(da==1) return printf("%d\n",pre),void();
printf("%d ",pre);
bool vis=0;
rep(i,2,da) {
if(f0[pre]==f1[pre]&&!vis) {
if(R1[pre]>R0[pre]) vis=1;
pre=max(R0[pre],R1[pre]);
}
else if(f1[pre]==da-i+2) {
pre=R1[pre];
vis=1;
}
else if(f0[pre]==da-i+2) {
pre=R0[pre];
}
if(i!=da) printf("%d ",pre);
else printf("%d\n",pre);
}
}
int main() {
//freopen("a.txt","r",stdin);
//freopen("a.out","w",stdout);
while (scanf("%d",&n)==1) {
rep(i,1,n) scanf("%d",a+i);
rep(i,1,n) p[i]=(node){a[i],i};
int cnt=0;
sort(p+1,p+n+1);
p[0].x=p[1].x-1;
rep(i,1,n) {
if(p[i].x!=p[i-1].x) ++cnt;
a[p[i].id]=cnt;
}
//rep(i,1,n) cout<<a[i]<<" "; cout<<endl;
per(i,n,1) {
//cout<<i<<endl;
ans[0]=0;
query(1,1,n,1,a[i]-1,1);
f1[i]=ans[0]+1;
L1[i]=ans[1],R1[i]=ans[2];
ans[0]=0;
query(1,1,n,a[i]+1,n,1);
query(1,1,n,a[i]+1,n,0);
f0[i]=ans[0]+1;
L0[i]=ans[1],R0[i]=ans[2];
T_add(1,1,n,a[i],i);
}
find_min();
find_max();
rep(i,1,4*n) Max[0][i]=Max[1][i]=0;
}
}
6595 Everything Is Generated In Equal Probability
我的做法是线性的
根据期望的线性行只考虑一个逆序对(i,j)的贡献即可
dp发现长度为n的逆序对贡献都是
4
3
\frac {4} {3}
34
直接算即可
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
typedef long long s64;
const int M=3e3+5;
const int mod=998244353;
int n;
int f[M],bin[M];
int fac[M],inv[M];
int qpow(int x,int k) {
int t=1;
for(;k;k>>=1,x=1ll*x*x%mod) if(k&1) t=1ll*t*x%mod;
return t;
}
int C(int x,int y) {
if(x<y) return 0;
return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;}
int main() {
//freopen("a.txt","r",stdin);
//freopen("a.out","w",stdout);
//cout<<554580197ll*9%mod<<endl;
f[1]=0;
bin[0]=1;
rep(i,1,3000) inc(bin[i]=bin[i-1],bin[i-1]);
fac[0]=1;
rep(i,1,3000) fac[i]=1ll*i*fac[i-1]%mod;
inv[3000]=qpow(fac[3000],mod-2);
per(i,3000,1) inv[i-1]=1ll*i*inv[i]%mod;
//神奇为啥f[i]都相等呢
/*
rep(i,2,3000) {
rep(j,2,i-1) inc(f[i],1ll*f[j]*C(i-2,i-j)%mod);
inc(f[i],bin[i]);
f[i]=1ll*f[i]*qpow(bin[i]-1,mod-2)%mod;
}
*/
//rep(i,1,10) cout<<f[i]<<" "; cout<<endl;
rep(i,2,3000)
inc(f[i]=f[i-1],1ll*C(i,2)*C(i,2)%mod*fac[i-2]%mod*332748119%mod*inv[i]%mod);
while (scanf("%d",&n)==1) printf("%lld\n",1ll*f[n]*qpow(n,mod-2)%mod);
}
6598 Harmonious Army
类似文理分科的网络流最小割
将每个人worrier
+
b
4
+\frac{b} {4}
+4b,Mage+
c
3
\frac{c}{3}
3c
剩下的按照文理分科来搞
注意worrier和mage要加上inf保证最小割一定会割
即不选择
b
2
+
c
3
\frac{b}{2}+\frac{c}{3}
2b+3c的贡献
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
typedef long long s64;
const int M=2e4+2000;
const int mod=998244353;
const s64 inf=1e15;
int n,m,S,T;
int e_size,head[M];
int dep[M];
s64 A[M],B[M];
struct edge{
int u;
int v;
s64 w;
int nxt;
}e[M*100];
void e_add(int u,int v,s64 w) {
e[++e_size]=(edge){u,v,w,head[u]};
head[u]=e_size;
}
void insert(int u,int v,s64 w) {
e_add(u,v,w),e_add(v,u,0);
}
bool Bfs() {
queue <int> q;
memset(dep,0,sizeof(T)*(T+5));
q.push(S);
dep[S]=1;
while (!q.empty()) {
int r=q.front();
q.pop();
for(int i=head[r];i;i=e[i].nxt) {
int v=e[i].v;
if(e[i].w&&!dep[v]) {
dep[v]=dep[r]+1;
if(v==T) return 1;
q.push(v);
}
}
}
return 0;
}
s64 Dfs(int x,s64 cp) {
if(x==T) return cp;
s64 las=cp,q;
for(int i=head[x];i;i=e[i].nxt) {
int v=e[i].v;
if(dep[v]==dep[x]+1&&e[i].w&&las) {
q=Dfs(v,min(e[i].w,las));
if(!q) {dep[v]=0;continue;}
las-=q,e[i].w-=q,e[i^1].w+=q;
}
}
return cp-las;
}
int main() {
//freopen("a.txt","r",stdin);
//freopen("a.out","w",stdout);
while (scanf("%d%d",&n,&m)==2) {
S=2*(n+m)+1,T=S+1;
e_size=1;
int cnt=2*n;
s64 ans=0;
rep(i,1,n) A[i]=B[i]=1e12;
rep(i,1,m) {
int u,v,a,b,c;
scanf("%d%d%d%d%d",&u,&v,&a,&b,&c);
ans+=a+c;
A[u]+=a/4,A[v]+=a/4;
B[u]+=c/3,B[v]+=c/3;
++cnt;
insert(S,cnt,a/2);
insert(cnt,2*u,inf);
insert(cnt,2*v,inf);
++cnt;
insert(cnt,T,c/3);
insert(2*u-1,cnt,inf);
insert(2*v-1,cnt,inf);
}
rep(i,1,n) insert(S,2*i-1,A[i]),insert(2*i-1,2*i,inf),insert(2*i,T,B[i]);
while (Bfs()) ans-=Dfs(S,inf);
ans+=1e12*n;
cout<<ans<<endl;
rep(i,1,T) A[i]=B[i]=head[i]=0;
}
}
6599 I Love Palindrome String
直接回文自动机算出现次数
dfs回文树用dfs性质判断
v
i
s
[
l
e
n
+
1
2
]
vis[\frac{len+1}{2}]
vis[2len+1]是否存在即可
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
typedef long long s64;
const int M=1e6+5;
int n;
char s[M];
int fail_[M];
bool vis[M];
int ans[M],len[M];
int head[M],e_size;
int node_num,nxt[M][26],f[M],las;
struct edge{
int v,nxt;
}e[M*2];
void e_add(int u,int v) {
e[++e_size]=(edge) {v,head[u]};
head[u]=e_size;
}
int get_fail(int x,int c) {
while (s[n]!=s[n-len[x]-1]) x=fail_[x];
return x;
}
void extend_(int c) {
int t=get_fail(las,c);
if(!nxt[t][c]) {
++node_num;
len[node_num]=len[t]+2;
fail_[node_num]=nxt[get_fail(fail_[t],c)][c];
nxt[t][c]=node_num;
}
las=nxt[t][c];
}
void dfs(int x) {
if(x!=1) vis[len[x]]=1;
for(int i=head[x];i;i=e[i].nxt) {
int v=e[i].v;
dfs(v);
f[x]+=f[v];
}
if(vis[(1+len[x])>>1]) ans[len[x]]+=f[x];
if(x!=1) vis[len[x]]=0;
}
int main() {
//freopen("a.txt","r",stdin);
while (scanf("%s",s+1)==1) {
s[0]=-1;
fail_[0]=1;
fail_[1]=1;
len[1]=-1;
las=1;
node_num=1;
int tot=strlen(s+1);
reverse(s+1,s+tot+1);
n=0;
rep(i,1,tot) {
++n;
extend_(s[i]-'a');
++f[las];
}
rep(i,0,node_num) if(fail_[i]!=i) e_add(fail_[i],i);
dfs(1);
rep(i,1,tot-1) printf("%d ",ans[i]);
printf("%d\n",ans[tot]);
e_size=0;
rep(i,0,node_num) f[i]=head[i]=ans[i]=fail_[i]=0,memset(nxt[i],0,sizeof(nxt[i]));
node_num=0;
}
}