鞍钢浐燮鑿
AGC001
[AGC001D]Arrays and Palindrome
全场最有含金量的题
不难发现奇数最多2个
然后把它们分别放在两端,让\(b_1=a_1+1\),\(b_n=a_n-1\),\(b_i=a_i,2\le i\le n-1\)
然后就行了
大概是因为这样错位了,所以\(b_i\)能保证\(i\)及以前的连在一起,且正好有一条外联边
[AGC001E] BBQ Hard
很典的转化
[AGC001F] Wide Swap
想到一个逆置换\(Q\)就简单了
相当于\(i,i+1\),能交换等价于\(|Q_i-Q_{i+1}|>k\)
为了使其最小,我发现交换相邻的逆序一定不劣
直接依次插\(Q\),用平衡树维护一下即可
Show Code
#include<bits/stdc++.h>
#define ls Tree[p].lc
#define rs Tree[p].rc
using namespace std;
const int MAXN=5e5+5;
int n,k;
int P[MAXN];
int Q[MAXN];
mt19937 Niuzi(998244353);
struct FHQ_node{
int mx,mi;
int lc,rc;
int Siz;
int ind;
int Key;
}Tree[MAXN];
int rt,cnt_node;
int New(int id)
{
++cnt_node;
Tree[cnt_node].Key=Niuzi();
Tree[cnt_node].ind=Tree[cnt_node].mi=Tree[cnt_node].mx=id;
Tree[cnt_node].lc=Tree[cnt_node].rc=0;
Tree[cnt_node].Siz=1;
return cnt_node;
}
void push_up(int p)
{
Tree[p].mi=Tree[p].ind;
Tree[p].mx=Tree[p].ind;
if(ls)
{
Tree[p].mi=min(Tree[p].mi,Tree[ls].mi);
Tree[p].mx=max(Tree[p].mx,Tree[ls].mx);
}
if(rs)
{
Tree[p].mi=min(Tree[p].mi,Tree[rs].mi);
Tree[p].mx=max(Tree[p].mx,Tree[rs].mx);
}
Tree[p].Siz=Tree[ls].Siz+Tree[rs].Siz+1;
}
void split(int p,int k,int &x,int &y)
{
if(!p)
{
x=0;
y=0;
return;
}
if(Tree[ls].Siz+1<=k)
{
x=p;
split(rs,k-(Tree[ls].Siz+1),rs,y);
}
else
{
y=p;
split(ls,k,x,ls);
}
push_up(p);
}
int Merge(int x,int y)
{
if((!x)||(!y))
{
return x+y;
}
if(Tree[x].Key<Tree[y].Key)
{
Tree[x].rc=Merge(Tree[x].rc,y);
push_up(x);
return x;
}
else
{
Tree[y].lc=Merge(x,Tree[y].lc);
push_up(y);
return y;
}
}
int Findp(int p,int x)
{
if(!p)
{
return 0;
}
if(Tree[p].ind==x)
{
return Tree[ls].Siz+1;
}
else if(Tree[ls].mx>=x&&Tree[ls].mi<=x)
{
return Findp(ls,x);
}
else
{
return Findp(rs,x)+Tree[ls].Siz+1;
}
}
int Find(int p,int r)
{
if(!p)
{
return 0;
}
int res=0;
//printf("%d %d %d %d--\n",l,r);
if(rs)
{
if(Tree[rs].mi>=r)
{
res+=Tree[rs].Siz;
if(Tree[p].ind>=r)
{
res+=1;
res+=Find(ls,r);
return res;
}
else
{
return res;
}
}
else
{
return Find(rs,r);
}
}
else
{
if(Tree[p].ind>=r)
{
res+=1;
res+=Find(ls,r);
return res;
}
else
{
return res;
}
}
}
int Ans[MAXN];
int id=0;
void dfs(int p)
{
if(!p)
{
return;
}
dfs(ls);
Ans[Tree[p].ind]=++id;
dfs(rs);
}
void Print(int p)
{
if(!p)
{
return;
}
Print(ls);
printf("%d ",Tree[p].ind);
Print(rs);
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&P[i]);
Q[P[i]]=i;
}
for(int i=1;i<=n;i++)
{
int kx=Find(rt,Q[i]+k);
int lx,rx;
split(rt,Tree[rt].Siz-kx,lx,rx);
rt=Merge(lx,Merge(New(Q[i]),rx));
}
dfs(rt);
for(int i=1;i<=n;i++)
{
printf("%d\n",Ans[i]);
}
}
AGC002
[AGC002F] Leftmost Ball
把出现的第一个位置看做\(0\),相当于是填\(n\)个\(0\),\(k-1\)个\(i\),要求每个\(i\)前面有个\(0\)匹配
考虑倒着填,设\(dp_{i,j}\)表示前\(i\)种颜色后面有\(j\)个\(0\)的方案数
没了??
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=2005;
int n,k;
int dp[MAXN][MAXN];
int s[MAXN][MAXN];
int fac[MAXN*MAXN*2];
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
int inv_fac[MAXN*MAXN*2];
int C(int n,int m)
{
if(n<m||m<0)
{
return 0;
}
if(n==m||m==0)
{
return 1;
}
return ((((long long)fac[n]*inv_fac[m])%MOD)*inv_fac[n-m])%MOD;
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
fac[0]=1;
for(int i=1;i<=MAXN*MAXN*2-5;i++)
{
fac[i]=((long long)fac[i-1]*i)%MOD;
}
inv_fac[MAXN*MAXN*2-5]=inv(fac[MAXN*MAXN*2-5],MOD);
for(int i=MAXN*MAXN*2-5-1;i>=1;i--)
{
inv_fac[i]=((long long)inv_fac[i+1]*(i+1))%MOD;
}
scanf("%d %d",&n,&k);
if(k==1)
{
printf("1\n");
return 0;
}
else
{
dp[0][0]=1;
s[0][0]=1;
for(int i=1;i<=n;i++)
{
s[0][i]=((long long)s[0][i-1]);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
{
int Rf=(i-1)*(k-1)+j;
int v=s[i-1][j];
v=((long long)v*C(Rf+k-2,Rf))%MOD;
dp[i][j]=((long long)dp[i][j]+v)%MOD;
}
for(int j=0;j<=n;j++)
{
if(j)
{
s[i][j]=((long long)s[i][j-1]+dp[i][j])%MOD;
}
else
{
s[i][j]=dp[i][j];
}
}
}
//printf("%d %d??\n",dp[2][0],dp[2][1]);
int Res=s[n][n-1];
for(int i=1;i<=n;i++)
{
Res=((long long)Res*i)%MOD;
}
printf("%d\n",Res);
}
}
[AGC002E] Candy Piles
题读错了/kk
考虑用二元组\((i,j)\)表示一个局面
\((i,j)=(i+1,j+1)\),感性理解一下
然后你就只用找边界即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n;
int a[MAXN];
int cmp(int x,int y)
{
return x>y;
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(i+1>a[i+1])
{
int Rest=a[i]-(i-1);
int Pi=i;
while(Pi<=n&&a[Pi]>=i)
{
++Pi;
}
int Rp=Pi-i;
if((Rest%2==0)||(Rp%2==0))
{
printf("First\n");
}
else
{
printf("Second\n");
}
break;
}
}
}
AGC003
[AGC003F] Fraction of Fractal
本来想着欧拉定理
结果这里直接分个类,面数只有\(1\)
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
struct Martix{
int val[2][2];
void clear()
{
memset(val,0,sizeof(val));
}
void init()
{
clear();
for(int i=0;i<2;i++)
{
val[i][i]=1;
}
}
Martix operator*(const Martix x)const{
Martix Res;
Res.clear();
for(int k=0;k<2;k++)
{
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
Res.val[i][j]=((long long)Res.val[i][j]+((long long)val[i][k]*x.val[k][j])%MOD)%MOD;
}
}
}
return Res;
}
}A,B;
Martix Pow(Martix Base,long long b)
{
Martix Res;
Res.init();
while(b)
{
//cerr<<b<<endl;
if(b&1)
{
Res=Res*Base;
}
Base=Base*Base;
b>>=1;
}
return Res;
}
int Pow(int a,long long b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int n,m;
long long k;
char s[2005][2005];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %lld",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
}
int tot=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(s[i][j]=='#')
{
++tot;
}
}
}
int Cp=0;
int Ca=0,Cb=0;
int Da=0;
int Db=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(s[i][j]!='#')
{
continue;
}
if((i+1<=n)&&(s[i+1][j]=='#'))
{
Da++;
}
else if(s[1][j]=='#'&&i==n)
{
Ca++;
}
if((j+1<=m)&&(s[i][j+1]=='#'))
{
Db++;
}
else if(s[i][1]=='#'&&j==m)
{
++Cb;
}
}
}
Cp=tot;
if(Ca&&Da&&Cb&&Db)
{
printf("1\n");
}
else if((!(Ca&&Da))&&(!(Cb&&Db)))
{
printf("%d\n",Pow(Cp,k-1,MOD));
}
else
{
if(k==1)
{
printf("1");
return 0;
}
int Res=Pow(Cp,k-1,MOD);
if((Ca&&Da))
{
A.clear();
A.val[0][0]=Da;A.val[0][1]=Ca;
B.clear();
B.val[0][0]=Cp;B.val[0][1]=0;
B.val[1][0]=Da;B.val[1][1]=Ca;
A=A*Pow(B,k-2);
Res=((long long)Res-A.val[0][0]+MOD)%MOD;
}
else
{
A.clear();
A.val[0][0]=Db;A.val[0][1]=Cb;
B.clear();
B.val[0][0]=Cp;B.val[0][1]=0;
B.val[1][0]=Db;B.val[1][1]=Cb;
A=A*Pow(B,k-2);
//printf("%d??\n",Res);
Res=((long long)Res-A.val[0][0]+MOD)%MOD;
}
printf("%d\n",Res);
}
}
[AGC003E] Sequential operations on Sequence
大脑萎缩/kk
先砍没用的
直接倒着做,计算出每次操作的系数即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n,m;
long long Q[MAXN];
int st[MAXN];
int head;
long long R[MAXN];
long long Cf[MAXN];
void solve(int x,long long B,long long Radio)
{
if(x==1)
{
//printf("%d %d???\n",B,Radio);
Cf[1]+=Radio;
Cf[B+1]-=Radio;
return;
}
long long Times=(B/Q[st[x-1]]);
R[x-1]+=(Times*Radio);
//printf("%d %d %d %d??\n",R[x-1],x-1,B,Q[st[x-1]]);
long long Rt=(B%Q[st[x-1]]);
int l=1;
int r=x-1;
int Key;
while(l<=r)
{
int mid=(l+r)>>1;
if(Q[st[mid]]>Rt)
{
r=mid-1;
Key=mid;
}
else
{
l=mid+1;
}
}
solve(Key,Rt,Radio);
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&m);
Q[0]=n;
st[++head]=0;
for(int i=1;i<=m;i++)
{
scanf("%lld",&Q[i]);
while(head&&Q[st[head]]>=Q[i])
{
head--;
}
st[++head]=i;
}
R[head]=1;
for(int i=head;i>=1;i--)
{
// printf("%d??\n",Q[st[i]]);
solve(i,Q[st[i]],R[i]);
}
for(int i=1;i<=n;i++)
{
Cf[i]=Cf[i-1]+Cf[i];
printf("%lld\n",Cf[i]);
}
}
[AGC004E] Salvage Robots
贪心得想,我们肯定是先往一个方向走到底再回去,这里回去的时候要考虑\(x,y\)的先后顺序得跑个\(dp\)
不过讨论的情况有点多
实际上我们直接记录\(4\)个状态即可,同时可以得到当前能前往的矩形区域直接转移就好了
这确实很暴力,如果想优化空间还得是上面的做法
Show Code
#include<bits/stdc++.h>
#define x1 kfks
#define y1 nyhweisuo
#define x2 xunjie
#define y2 nyhtebieweisuo
using namespace std;
const int MAXN=105;
int n,m;
char mp[MAXN][MAXN];
short sum[MAXN][MAXN];
short dp[MAXN][MAXN][MAXN][MAXN];
short Get(int x1,int y1,int x2,int y2)
{
if(x1>x2)
{
return 0;
}
if(y1>y2)
{
return 0;
}
return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",mp[i]+1);
for(int j=1;j<=m;j++)
{
sum[i][j]=sum[i-1][j]+sum[i][j-1]+(mp[i][j]=='o')-sum[i-1][j-1];
}
}
int sx,sy;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j]=='E')
{
sx=i;
sy=j;
}
}
}
short Res=0;
for(int upx=0;upx<=n;upx++)
{
for(int dox=0;dox<=n;dox++)
{
for(int upy=0;upy<=m;upy++)
{
for(int doy=0;doy<=m;doy++)
{
Res=max(Res,dp[upx][dox][upy][doy]);
int Lx=upx+1;
int Rx=n-dox;
int Ly=upy+1;
int Ry=m-doy;
int lx=sx-dox;
int rx=sx+upx;
int ly=sy-doy;
int ry=sy+upy;
lx=max(lx,Lx);
ly=max(ly,Ly);
rx=min(rx,Rx);
ry=min(ry,Ry);
if(rx<Rx)
{
dp[upx+1][dox][upy][doy]=max((int)dp[upx+1][dox][upy][doy],dp[upx][dox][upy][doy]+Get(rx+1,ly,rx+1,ry));
}
if(lx>Lx)
{
dp[upx][dox+1][upy][doy]=max((int)dp[upx][dox+1][upy][doy],dp[upx][dox][upy][doy]+Get(lx-1,ly,lx-1,ry));
}
if(ry<Ry)
{
dp[upx][dox][upy+1][doy]=max((int)dp[upx][dox][upy+1][doy],dp[upx][dox][upy][doy]+Get(lx,ry+1,rx,ry+1));
}
if(ly>Ly)
{
dp[upx][dox][upy][doy+1]=max((int)dp[upx][dox][upy][doy+1],dp[upx][dox][upy][doy]+Get(lx,ly-1,rx,ly-1));
}
}
}
}
}
printf("%d\n",(int)Res);
}
AGC005
[AGC005F] Many Easy Problems
想复杂了/kk
考虑每个点的贡献,反着考虑,发现必须要求选的点要么全在子树内要么在子树外
直接列式子,\(\sum\limits_j{\binom{n}{i}-\sum\limits_{v}\binom{Siz_v}{i}-\binom{Siz_j}{i}}\)
这玩意拆一下就是个卷积
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
const int MOD=924844033;
int Rev[MAXN*4];
int Pow(int a,int b,int p)
{
int res=1;
int base=a;
while(b)
{
if(b&1)
{
res=((long long)res*base)%p;
}
base=((long long)base*base)%p;
b>>=1;
}
return res;
}
int fac[MAXN];
int inv(int a,int p)
{
return Pow(a,p-2,p);
}
const int g=5;
struct Poly{
vector<int>V;
void NTT(int Limit,int type)
{
int Len=(1<<Limit);
for(int i=0;i<Len;i++)
{
Rev[i]=((Rev[i>>1]>>1)|((i&1)<<(Limit-1)));
}
while(V.size()<Len)
{
V.push_back(0);
}
for(int i=0;i<Len;i++)
{
if(i<Rev[i])
{
swap(V[i],V[Rev[i]]);
}
}
for(int l=1;l<Len;l<<=1)
{
int Wn=Pow(g,(MOD-1)/(l<<1),MOD);
if(type==-1)
{
Wn=inv(Wn,MOD);
}
for(int i=0;i<Len;i+=(l<<1))
{
int W=1;
for(int j=i;j<i+l;j++,W=((long long)W*Wn)%MOD)
{
int Xc=V[j];
int Yc=((long long)V[j+l]*W)%MOD;
V[j]=((long long)Xc+Yc)%MOD;
V[j+l]=((long long)Xc-Yc+MOD)%MOD;
}
}
}
if(type==-1)
{
int Liv=inv(Len,MOD);
for(int i=0;i<Len;i++)
{
V[i]=((long long)V[i]*Liv)%MOD;
}
}
}
};
Poly operator*(Poly A,Poly B){
int N=A.V.size();
int M=B.V.size();
int nox=1;
int Lm=0;
while(nox<=(N+M-2))
{
nox<<=1;
Lm++;
}
A.NTT(Lm,1);
B.NTT(Lm,1);
for(int i=0;i<nox;i++)
{
A.V[i]=((long long)A.V[i]*B.V[i])%MOD;
}
A.NTT(Lm,-1);
while(A.V.size()>(N+M-1))
{
A.V.pop_back();
}
return A;
};
int n;
int x,y;
vector<int>G[MAXN];
int Siz[MAXN];
void dfs(int x,int f)
{
Siz[x]=1;
for(int i=0;i<G[x].size();i++)
{
int v=G[x][i];
if(v==f)
{
continue;
}
dfs(v,x);
Siz[x]+=Siz[v];
}
}
int Num[MAXN];
int Ca[MAXN];
int Cb[MAXN];
int Cc[MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1,0);
for(int i=1;i<=n;i++)
{
if(i==1)
{
continue;
}
Num[Siz[i]]++;
}
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=((long long)fac[i-1]*i)%MOD;
}
Poly A,B;
A.V.resize(n+1);
B.V.resize(n+1);
for(int i=0;i<=n;i++)
{
A.V[i]=((long long)Num[i]*fac[i])%MOD;
}
for(int i=0;i<=n;i++)
{
B.V[n-i]=((long long)inv(fac[i],MOD))%MOD;
}
A=(A*B);
for(int i=1;i<=n;i++)
{
int Res=A.V[n+i];
Res=((long long)Res*inv(fac[i],MOD))%MOD;
Cb[i]=Res;
Ca[i]=((long long)n*fac[n])%MOD;
Ca[i]=((long long)Ca[i]*inv(fac[i],MOD))%MOD;
Ca[i]=((long long)Ca[i]*inv(fac[n-i],MOD))%MOD;
}
for(int i=0;i<=n;i++)
{
Num[i]=0;
}
for(int i=1;i<=n;i++)
{
Num[n-Siz[i]]++;
}
A.V.resize(n+1,0);
B.V.resize(n+1,0);
for(int i=0;i<=n;i++)
{
A.V[i]=((long long)Num[i]*fac[i])%MOD;
}
for(int i=0;i<=n;i++)
{
B.V[n-i]=((long long)inv(fac[i],MOD))%MOD;
}
A=(A*B);
for(int i=1;i<=n;i++)
{
int Res=A.V[n+i];
Res=((long long)Res*inv(fac[i],MOD))%MOD;
Cc[i]=Res;
///printf("%d %d %d %d\n",i,Ca[i],Cb[i],Cc[i]);
printf("%d\n",((long long)Ca[i]-Cb[i]-Cc[i]+2ll*MOD)%MOD);
}
}
[AGC005E] Sugigma: The Showdown
先考虑什么时候\(-1\)
然后你发现就是找一条边\(u,v\)使得能到达且两点间距离\(>3\)
能到达就看距离是否\(A<B\)然后\(bfs\)一下
然后再考虑不是\(-1\)
由于没有上面一种情况,因此就没有类似欺骗的操作,直接选能到达里离\(B\)最远的
证明不会
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n,rx,ry;
vector<int>G[MAXN];
vector<int>g[MAXN];
int x,y;
int depa[MAXN];
int depb[MAXN];
void dfs1(int x,int f)
{
for(int i=0;i<G[x].size();i++)
{
int v=G[x][i];
if(v==f)
{
continue;
}
depa[v]=depa[x]+1;
dfs1(v,x);
}
}
int cnt_dfn;
int Rlf[MAXN];
int dfn[MAXN];
int dp[MAXN][21];
int Lg[MAXN];
int Fa[MAXN];
void dfs2(int x,int f)
{
dfn[x]=++cnt_dfn;
Rlf[cnt_dfn]=x;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
Fa[v]=x;
depb[v]=depb[x]+1;
dfs2(v,x);
}
}
int V[MAXN];
int E[MAXN];
void dfs3(int x,int f)
{
if(!V[x])
{
return;
}
E[x]=1;
for(int i=0;i<G[x].size();i++)
{
int v=G[x][i];
if(v==f)
{
continue;
}
dfs3(v,x);
}
}
int LCA(int a,int b)
{
if(a==b)
{
return a;
}
int l=dfn[a];
int r=dfn[b];
if(l>r)
{
swap(l,r);
}
l++;
int k=Lg[r-l+1];
int tox=min(dp[l][k],dp[r-(1<<k)+1][k]);
return Rlf[tox];
}
int dist(int a,int b)
{
return depb[a]+depb[b]-2*depb[LCA(a,b)];
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&rx,&ry);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(rx,0);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs2(ry,0);
for(int i=1;i<=n;i++)
{
if(depb[i]>depa[i])
{
V[i]=1;
}
}
for(int i=1;i<=n;i++)
{
Lg[i]=log2(i);
dp[i][0]=dfn[Fa[Rlf[i]]];
}
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
dfs3(rx,0);
bool f=0;
for(int i=1;i<=n;i++)
{
if(E[i])
{
for(int j=0;j<G[i].size();j++)
{
int v=G[i][j];
if(dist(i,v)>=3)
{
//printf("%d %d??\n",i,v);
f=1;
}
}
}
}
if(f)
{
printf("-1");
}
else
{
int Res=0;
for(int i=1;i<=n;i++)
{
if(E[i])
{
Res=max(Res,2*depb[i]);
}
}
printf("%d\n",Res);
}
}
[AGC005D] ~K Perm Counting
容斥
然后直接\(dp\),每\(2k\)分一组然后直接暴力卷积即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=924844033;
const int MAXN=2005;
int n,k;
int dp[MAXN];
int tmp[MAXN];
int f[MAXN][MAXN][2];
int fac[MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d",&n,&k);
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=((long long)fac[i-1]*i)%MOD;
}
dp[0]=1;
for(int l=1;l<=min(n,2*k);l++)
{
int Cp=0;
for(int i=l;i<=n;i+=2*k)
{
Cp++;
}
for(int i=0;i<=Cp;i++)
{
for(int j=0;j<=Cp;j++)
{
f[i][j][0]=f[i][j][1]=0;
}
}
f[0][0][0]=1;
for(int i=1;i<=Cp;i++)
{
int id=l+(i-1)*2*k;
for(int j=0;j<=Cp;j++)
{
if(j)
{
if(id+k<=n)
{
f[i][j][1]=((long long)f[i][j][1]+f[i-1][j-1][0])%MOD;
f[i][j][1]=((long long)f[i][j][1]+f[i-1][j-1][1])%MOD;
}
if(id-k>=1)
{
f[i][j][0]=((long long)f[i][j][0]+f[i-1][j-1][0])%MOD;
}
}
f[i][j][0]=((long long)f[i][j][0]+f[i-1][j][0])%MOD;
f[i][j][0]=((long long)f[i][j][0]+f[i-1][j][1])%MOD;
}
}
for(int i=0;i<=n;i++)
{
tmp[i]=dp[i];
dp[i]=0;
}
for(int i=0;i<=n;i++)
{
for(int j=0;j<=min(i,Cp);j++)
{
int vx=((long long)f[Cp][j][0]+f[Cp][j][1])%MOD;
dp[i]=((long long)dp[i]+((long long)vx*tmp[i-j])%MOD)%MOD;
}
}
}
int Res=0;
for(int i=0;i<=n;i++)
{
int v=dp[i];
v=((long long)v*fac[n-i])%MOD;
if(i&1)
{
Res=((long long)Res-v+MOD)%MOD;
}
else
{
Res=((long long)Res+v)%MOD;
}
}
printf("%d\n",Res);
}
AGC006
[AGC006E] Rotate 3x3
不难将其刻画成\(n\)个有正负的数每次可以选择\(a_i,a_{i+2}\)交换并将\(i,i+1,i+2\)取\(-\),使其排序成\(1-n\)
首先奇偶分类,判断其不管正负是否能排
然后你可以通过一些操作使得对\(i,i+2\)取\(-\)(不会构造
然后你可以发现奇偶负号只与其奇偶性有关
同时你可以发现奇数中的操作次数会对偶数的负号个数有影响,所以只用判逆序对奇偶即可
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n;
int a[MAXN][3];
pair<int,int>v[MAXN];
int cf[MAXN];
int Pos[MAXN];
int Px[MAXN];
int Bit[MAXN];
int lowbit(int x)
{
return x&(-x);
}
void update(int k,int x)
{
for(int i=k;i>=1;i-=lowbit(i))
{
Bit[i]+=x;
}
return;
}
int Sum(int k)
{
int res=0;
for(int i=k;i<=n;i+=lowbit(i))
{
res+=Bit[i];
}
return res;
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=3;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[j][i]);
}
}
for(int i=1;i<=n;i++)
{
if(a[i][1]<a[i][2]&&a[i][2]<a[i][3])
{
if((a[i][1]-1)%3==0)
{
v[i]=make_pair((a[i][1]-1)/3+1,0);
}
else
{
printf("No");
return 0;
}
}
else if(a[i][1]>a[i][2]&&a[i][2]>a[i][3])
{
if((a[i][3]-1)%3==0)
{
v[i]=make_pair((a[i][3]-1)/3+1,1);
}
else
{
printf("No");
return 0;
}
}
else
{
printf("No");
return 0;
}
}
for(int i=1;i<=n;i++)
{
if((i&1)!=(v[i].first&1))
{
printf("No");
return 0;
}
Pos[v[i].first]=i;
}
long long Ro=0;
long long Re=0;
for(int i=1;i<=n;i+=2)
{
Px[i]=Sum(v[i].first);
Ro+=Px[i];
update(v[i].first,1);
if(v[i].second)
{
Re--;
}
}
memset(Bit,0,sizeof(Bit));
for(int i=2;i<=n;i+=2)
{
Px[i]=Sum(v[i].first);
Re+=Px[i];
update(v[i].first,1);
if(v[i].second)
{
Ro--;
}
}
if(Ro<0)
{
Ro=-Ro;
}
if(Re<0)
{
Re=-Re;
}
if(Ro%2==1)
{
printf("No\n");
return 0;
}
if(Re%2==1)
{
printf("No\n");
return 0;
}
printf("Yes\n");
}
[AGC006C] Rabbit Exercise
对于操作\(-a_i+a_{i-1}+a_{i+1}\),即差分\(d_i,d_i+1\)交换
然后直接置换环搞一下即可
Show Code
// LUOGU_RID: 139873219
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,m;
long long k;
int a[MAXN];
int d[MAXN];
int p[MAXN];
int vis[MAXN];
int Lorp[MAXN];
int Cnt;
void dfs(int x)
{
if(vis[x])
{
return;
}
vis[x]=1;
Lorp[Cnt++]=x;
dfs(p[x]);
}
int v[MAXN];
signed main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
d[i]=a[i]-a[i-1];
}
scanf("%lld %lld",&m,&k);
for(int i=1;i<=n;i++)
{
p[i]=i;
}
for(int i=1;i<=m;i++)
{
int x;
scanf("%lld",&x);
swap(p[x],p[x+1]);
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
Cnt=0;
dfs(i);
for(int i=0;i<Cnt;i++)
{
v[Lorp[i]]=d[Lorp[(i+k)%Cnt]];
}
}
}
long long now=0;
for(int i=1;i<=n;i++)
{
now+=v[i];
printf("%lld\n",now);
}
}
AGC007
[AGC007C] Pushing Balls
操作一次中间的线段会使\(i+2,i+3,i\)替代\(i\)
而我们操作\(i\)之前的会使\(i\)变为\(i+2\)
直接列出\(d'_i\)的期望
\(d'_i=d_i+\dfrac{i(d_{i+2}-d_i)+(d_{i+3}+d_{i+2})}{2n}\)
即\(d'_i=d_1+(i)x+\dfrac{i(2x)+d_1+(i+3)x+d_1+(i+2)x}{2n}\)
\(=d_1+ix+\dfrac{4ix+2d_1+5x}{2n}\)
发现\(d'\)依旧是等差数列
Show Code
#include<bits/stdc++.h>
using namespace std;
int n;
double d,x;
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %lf %lf",&n,&d,&x);
double Res=0;
while(n)
{
Res+=d+(2*n-1)*x*1.0/2;
double tpd=d;
double tpx=x;
d=tpd+(2*tpd+5*tpx)*1.0/(2*n);
x=x+2*tpx*1.0/n;
//x+=1;
n--;
}
printf("%.10lf\n",Res);
}
[AGC007D] Shik and Game
设\(dp_i\)为走完前\(i\)个点最小时间
转移线段树优化一下即可
不过好像可以单调队列
Show Code
#include<bits/stdc++.h>
#define ls Tree[p].lc
#define rs Tree[p].rc
using namespace std;
const int MAXN=5e5+5;
int n,E,T;
int x[MAXN];
long long dp[MAXN];
struct Seg_node{
int lc,rc;
long long date;
};
struct Seg{
Seg_node Tree[MAXN*10];
int rt;
int cnt_node;
void Insert(int &p,int l,int r,int k,long long x)
{
if(!p)
{
p=++cnt_node;
Tree[p].lc=Tree[p].rc=0;
Tree[p].date=2e18;
}
Tree[p].date=min(Tree[p].date,x);
if(l==r)
{
return;
}
int mid=((long long)l+r)>>1;
if(k<=mid)
{
Insert(ls,l,mid,k,x);
}
else
{
Insert(rs,mid+1,r,k,x);
}
}
long long Query(int p,int l,int r,int ql,int qr)
{
if(ql>qr)
{
return 2e18;
}
if(!p)
{
return 2e18;
}
if(l>=ql&&r<=qr)
{
return Tree[p].date;
}
long long Res=2e18;
int mid=((long long)l+r)>>1;
if(ql<=mid)
{
Res=min(Res,Query(ls,l,mid,ql,qr));
}
if(qr>mid)
{
Res=min(Res,Query(rs,mid+1,r,ql,qr));
}
return Res;
}
}t1,t2;
int s[MAXN];
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d %d %d",&n,&E,&T);
for(int i=1;i<=n;i++)
{
scanf("%d",&x[i]);
}
x[0]=0;
for(int i=0;i<n;i++)
{
s[i]=s[i-1]+(x[i+1]-x[i]);
}
dp[0]=0;
t1.Insert(t1.rt,0,2e9,2*s[0],(x[1]-x[0])-s[0]-2*s[0]);
t2.Insert(t2.rt,0,2e9,2*s[0],(x[1]-x[0])-s[0]);
for(int i=1;i<=n;i++)
{
int Lit=2*s[i-1]-T;
dp[i]=t1.Query(t1.rt,0,2e9,0,Lit)+3ll*s[i-1];
dp[i]=min(dp[i],t2.Query(t2.rt,0,2e9,max(Lit+1,0),2e9)+s[i-1]+T);
// printf("%lld\n",t1.Query(t1.rt,0,2e9,0,Lit));
t1.Insert(t1.rt,0,2e9,2*s[i],dp[i]+(x[i+1]-x[i])-s[i]-2*s[i]);
t2.Insert(t2.rt,0,2e9,2*s[i],dp[i]+(x[i+1]-x[i])-s[i]);
//printf("%d %lld %d?\n",i,dp[i],Lit);
}
printf("%lld\n",E-x[n]+dp[n]);
}
[AGC007E] Shik and Travel
一直在想怎么优化贪心/kk
实际上这里我们可以用\((a,b)\)来记录所有转移点
注意\((c,d)\),如果\(a<c,b<d\),则\((c,d)\)无用
这样按\(a\)排序,\(b\)则单减
考虑用\((a,b),(c,d)\)拼接为\((a,d)\),可以发现固定\(a\),\(d\)一定
这样一次转移我们的转移点数量不会超过\(2min(Siz_x,Siz_y)\)
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n;
vector<pair<int,int> >g[MAXN];
int x,w;
vector<pair<long long,long long> >V[MAXN];
long long Lit;
void dfs(int x)
{
if(!g[x].size())
{
V[x].clear();
V[x].push_back(make_pair(0,0));
return;
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i].first;
dfs(v);
}
int ls=g[x][0].first;
int rs=g[x][1].first;
int wl=g[x][0].second;
int wr=g[x][1].second;
V[x].clear();
int Pi=0;
vector<pair<long long,long long> >L,R;
for(int i=0;i<V[ls].size();i++)
{
auto tmp=V[ls][i];
while(Pi<V[rs].size()&&V[rs][Pi].first+wr+tmp.second+wl<=Lit)
{
Pi++;
}
if(Pi)
{
L.push_back(make_pair(V[ls][i].first+wl,V[rs][Pi-1].second+wr));
}
}
Pi=0;
for(int i=0;i<V[rs].size();i++)
{
auto tmp=V[rs][i];
while(Pi<V[ls].size()&&V[ls][Pi].first+wr+tmp.second+wl<=Lit)
{
Pi++;
}
if(Pi)
{
R.push_back(make_pair(V[rs][i].first+wr,V[ls][Pi-1].second+wl));
}
}
V[x].clear();
int Pl=0;
int Pr=0;
vector<pair<long long,long long> >Tmp;
while(Pl<L.size()&&Pr<R.size())
{
if(L[Pl]<R[Pl])
{
Tmp.push_back(L[Pl]);
Pl++;
}
else
{
Tmp.push_back(R[Pr]);
Pr++;
}
}
while(Pl<L.size())
{
Tmp.push_back(L[Pl]);
Pl++;
}
while(Pr<R.size())
{
Tmp.push_back(R[Pr]);
Pr++;
}
for(int i=0;i<Tmp.size();i++)
{
if(!V[x].size())
{
V[x].push_back(Tmp[i]);
}
else if(Tmp[i].second<V[x].back().second)
{
V[x].push_back(Tmp[i]);
}
}
//cerr<<x<<" "<<V[x].size()<<endl;
}
bool check(long long mid)
{
Lit=mid;
dfs(1);
return V[1].size()>=1;
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%d %d",&x,&w);
g[x].push_back(make_pair(i,w));
}
//printf("%d?\n",check(4));
//printf("%d?\n",check(6));
long long l=0;
long long r=4e10;
long long Key=-1;
while(l<=r)
{
long long mid=(l+r)>>1;
//printf("%lld %d?\n",mid,check(mid));
if(check(mid))
{
Key=mid;
r=mid-1;
}
else
{
l=mid+1;
}
}
printf("%lld\n",Key);
}
[AGC007F] Shik and Copying String
大抵是考虑用队列维护出从大到小的每个拐点的信息
这个感觉多WA一下就可以了
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n;
char s[MAXN];
char t[MAXN];
vector<int>Rec[27];
int Rs[MAXN];
int st[MAXN];
int head,tail;
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
scanf("%s",s+1);
scanf("%s",t+1);
while(n>=1&&s[n]==t[n])
{
n--;
}
if(n==0)
{
printf("0\n");
return 0;
}
for(int i=1;i<=n;i++)
{
Rec[s[i]-'a'].push_back(i);
}
if(!Rec[t[n]-'a'].size())
{
printf("-1");
return 0;
}
int R=Rec[t[n]-'a'].back();
Rs[n]=R;
for(int i=n-1;i>=1;i--)
{
int tox=upper_bound(Rec[t[i]-'a'].begin(),Rec[t[i]-'a'].end(),min(i,R))-Rec[t[i]-'a'].begin()-1;
if(tox>=0)
{
tox=Rec[t[i]-'a'][tox];
Rs[i]=tox;
R=Rs[i];
}
else
{
printf("-1\n");
return 0;
}
}
int Res=0;
head=1;
tail=0;
for(int i=n;i>=1;i--)
{
if(t[i]==t[i-1])
{
continue;
}
int Rx=Rs[i];
while(head<=tail&&st[head]-(tail-head+1)>=i)//偏移量为时间戳
{
head++;
}
if(Rx!=i)
{
st[++tail]=Rx;
Res=max(Res,tail-head+1);
}
}
printf("%d\n",Res+1);
}
AGC008
[AGC008F] Black Radius
一个很有AT风格的题
如果全有,不难想到对于每个\(u\)计数\(d\)最小的
会不会出现\(f(u_1,d)=f(u_2,d)\)的情况?
把全局刨掉就不会
不难看出合法的\(d\)是一段区间
考虑\((u,d)\)不合法,也即存在\(f(u,d)=f(v,d-1)\),\(v\in son_u\),或者\(d\ge Max_u\)
不难发现对于\(u\),如果找到一个点\(x\)使得\(d(u,x)>d-2\)则\(f(v,d-1)\not=f(u,d)\)
这里取\(d(u,v)=Max_u\),则\(d(u,x)=Sec_u\)
那如果\(d\ge d(u,x)+1\)就一定不合法吗\(?\),似乎是的...
对于不全有,我们考虑对\(u,s_u=0\),找到\(v,s_v=1\)与之对应,使得\(f(u,d)=f(v,d+dis(u,v))\)
这里不加证明的给出结论,\(v\)是以\(u\)为根时最大深度最小子树内的点
意会一下把
Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n;
int x,y;
vector<int>g[MAXN];
char s[MAXN];
int F1[MAXN];
int F2[MAXN];
int G2[MAXN];
int H1[MAXN];
int H2[MAXN];
int Fa[MAXN];
int Siz[MAXN];
void dfs1(int x,int f)
{
H1[x]=0x3f3f3f3f;
Siz[x]=(s[x]=='1');
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
Fa[v]=x;
dfs1(v,x);
F1[x]=max(F1[x],F1[v]+1);
Siz[x]+=Siz[v];
if(Siz[v])
{
H1[x]=min(H1[x],F1[v]+1);
}
}
}
void dfs2(int x,int f)
{
int Maxi=F2[x];
int Seci=0;
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
int Rw=F1[v]+1;
if(Rw>=Maxi)
{
Seci=Maxi;
Maxi=Rw;
}
else if(Rw>=Seci)
{
Seci=Rw;
}
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
int Shox;
int Rw=F1[v]+1;
if(Rw==Maxi)
{
Shox=Seci;
}
else
{
Shox=Maxi;
}
F2[v]=Shox+1;
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
H2[v]=0x3f3f3f3f;
if(Siz[1]-Siz[v])
{
H2[v]=F2[v];
}
}
for(int i=0;i<g[x].size();i++)
{
int v=g[x][i];
if(v==f)
{
continue;
}
dfs2(v,x);
}
}
int main()
{
// freopen("date.in","r",stdin);
// freopen("date.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
scanf("%s",s+1);
dfs1(1,0);
H2[1]=0x3f3f3f3f;
dfs2(1,0);
for(int i=1;i<=n;i++)
{
int Maxi=F2[i];
int Seci=0;
for(int j=0;j<g[i].size();j++)
{
int v=g[i][j];
if(v==Fa[i])
{
continue;
}
int Rw=F1[v]+1;
if(Rw>=Maxi)
{
Seci=Maxi;
Maxi=Rw;
}
else if(Rw>=Seci)
{
Seci=Rw;
}
}
G2[i]=Seci;
}
long long Res=0;
for(int i=1;i<=n;i++)
{
int H=min(H1[i],H2[i]);
if(s[i]=='1')
{
H=0;
}
int R=min(max(F1[i],F2[i]),G2[i]+2)-1;
// printf("%d %d %d %d\n",i,H,R,H1[i]);
if(H<=R)
{
Res+=(R-H+1);
}
}
printf("%lld\n",Res+1);
}