CF1292 div.1 做题记录
A
若某一列的第 \(i\) 行禁止移动,那么看另一列的第 \(i-1,i,i+1\) 行是否存在禁止移动的格子,若存在为 No
,否则为 Yes
,这个只需要在改变一个点状态时判断即可。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=1e5+10;
int n,m,f[2][N],s;
void solve(){
read(n),read(m);
for(int i=1;i<=m;i++){
int a,b;
read(a),read(b);
a--;
f[a][b]^=1;
if(f[a][b]){
if(f[a^1][b+1]){
s++;
}
if(f[a^1][b]){
s++;
}
if(f[a^1][b-1]){
s++;
}
}
else{
if(f[a^1][b+1]){
s--;
}
if(f[a^1][b]){
s--;
}
if(f[a^1][b-1]){
s--;
}
}
if(s){
puts("No");
}
else{
puts("Yes");
}
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
B
可以发现有可能到达的点不超过 \(60\) 个,将其排好序。显然的是若取到点 \(l\) 和点 \(r\),那么必然取得到区间 \([l,r]\) 内的所有点,因为是曼哈顿距离,点 \(l\) 到点 \(r\) 的最短路径中必然有经过其它点的路径,直接区间dp,求出从起始点开始遍历所有点的最短距离是多少。
最后找到区间长度最长且满足最短距离小于 \(t\) 的 \([l,r]\) 的区间长度。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int __int128
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=100,MX=2e16;
int x[N],y[N],ax,ay,bx,by,stx,sty,t,n,f[N][N][2];
int Abs(int x){
if(x<0){
return -x;
}
return x;
}
void solve(){
read(x[0]),read(y[0]),read(ax),read(ay),read(bx),read(by),read(stx),read(sty),read(t);
while(1){
n++;
x[n]=x[n-1]*ax+bx;
y[n]=y[n-1]*ay+by;
if(x[n]*ax>MX||y[n]*ay>MX){
break;
}
}
memset(f,0x3f,sizeof(f));
for(int i=0;i<=n;i++){
f[i][i][0]=f[i][i][1]=Abs(stx-x[i])+Abs(sty-y[i]);
}
for(int len=2;len<=n+1;len++){
for(int l=0;l<=n-len+1;l++){
int r=l+len-1;
f[l][r][0]=min(f[l][r][0],min(f[l+1][r][0]+Abs(x[l+1]-x[l])+Abs(y[l+1]-y[l]),f[l+1][r][1]+Abs(x[r]-x[l])+Abs(y[r]-y[l])));
f[l][r][1]=min(f[l][r][1],min(f[l][r-1][0]+Abs(x[r]-x[l])+Abs(y[r]-y[l]),f[l][r-1][1]+Abs(x[r]-x[r-1])+Abs(y[r]-y[r-1])));
}
}
int ans=0;
for(int i=0;i<=n;i++){
for(int j=i;j<=n;j++){
if(f[i][j][0]<=t||f[i][j][1]<=t){
ans=max(ans,j-i+1);
}
}
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
C
题面要求的是:
其中 \(\operatorname{mex}\) 表示集合中没有出现过的最小自然数(从 \(0\) 开始),\(S_{(s,t)}\) 表示路径 \((s,t)\) 上所有边的边权组成的集合。
对于这样的一个形式,有一个非常常见的变形,将其转化为:
也就是
可以发现这就相当于给一条链赋值,求经过其中的 \([0,x]\) 这一段的路径条数并对所有 \(x\) 求和。
假定我们已经知道是对哪条链赋值,\([0,x]\) 肯定位于一段连续的路径上。若不位于一段连续路径上,这段路径中多了一个 \(y(y>x)\),那么调整一下,将 \(y\) 放到路径端点。经过 \([0,x]\) 的路径数不会减少,经过 \([0,y]\) 的路径数不会增多,显然这是不劣的。
那么这显然变成了一个区间dp问题,可以发现这题支持 \(O(n^2)\) 暴力dp。令 \(f_{u,v}\) 表示对 \((u,v)\) 这条链赋值后答案的最大值,\(fa_{u,v}\) 表示以 \(u\) 为根是 \(v\) 的父亲,\(siz_{u,v}\) 表示 \(u\) 为根时 \(v\) 的子树大小。\(f_{u,v}=\max(f_{u,fa_{u,v}},f_{fa_{v,u},v})+siz_{u,v}\times siz_{v,u}\),最后对于所有 \(f_{u,v}\) 取 \(\max\) 即为答案。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=3010;
int n,siz[N][N],fa[N][N];
ll f[N][N];
vector<int>e[N];
void dfs(int u,int father,int rt){
fa[rt][u]=father;
siz[rt][u]=1;
for(auto v:e[u]){
if(v==father){
continue;
}
dfs(v,u,rt);
siz[rt][u]+=siz[rt][v];
}
}
ll work(int u,int v){
if(u==v){
return 0;
}
if(f[u][v]){
return f[u][v];
}
f[u][v]=max(work(fa[v][u],v)+1ll*siz[u][v]*siz[v][u],work(u,fa[u][v])+1ll*siz[v][u]*siz[u][v]);
return f[u][v];
}
void solve(){
read(n);
for(int i=1,u,v;i<n;i++){
read(u),read(v);
e[u].pb(v);
e[v].pb(u);
}
for(int i=1;i<=n;i++){
dfs(i,0,i);
}
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans=max(ans,work(i,j));
}
}
write_endl(ans);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}
D
可以发现这是一棵无限树,显然不可以在原树上做这个问题。可以发现本质的 \(k\) 最多只有 \(3000\) 个,可以尝试建出虚树。这棵树和阶乘的性质都比较有趣,若我们已经知道包含 \((i-1)!\) 的虚树,现在加入 \(i!\) 这个点。这两个点的差距只是多乘了一个 \(i\)。若 \(i\) 为质数,那么这两个点的 lca
一定为 \(1\) 号点。若 \(i\) 不为质数,那么原树上两点的 lca
肯定是从 \(1\) 号点开始一直走 \((i-1)!\) 的最大质因子的边,直到走完所有大于等于 \(i\) 的最大质因子的边。可以用树状数组维护每个质因子的出现次数,那么每个点和它们的 lca
的深度均可以算出来,虚树上任意相邻两点间边的长度也可以算出来。最后答案即为这棵虚树的带权重心。
点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
#define int long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
using namespace std;
namespace IO{
template<typename T>
inline void read(T &x){
x=0;
int f=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch-'0');
ch=getchar();
}
x=(f==1?x:-x);
}
template<typename T>
inline void write(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>=10){
write(x/10);
}
putchar(x%10+'0');
}
template<typename T>
inline void write_endl(T x){
write(x);
putchar('\n');
}
template<typename T>
inline void write_space(T x){
write(x);
putchar(' ');
}
}
using namespace IO;
const int N=2e4+10,inf=1e18;
int n,val[N],prime[N],min_prime[N],min_id[N],cnt;
void init(int mx){
for(int i=2;i<=mx;i++){
if(!min_prime[i]){
prime[++cnt]=i;
min_prime[i]=i;
min_id[i]=cnt;
}
for(int j=1;j<=cnt&&prime[j]*i<=mx;j++){
min_prime[i*prime[j]]=prime[j];
min_id[i*prime[j]]=j;
if(i%prime[j]==0){
break;
}
}
}
}
namespace Fenwick_Tree{
int c[N];
int lowbit(int x){
return x&(-x);
}
void update(int x,int val){
while(x){
c[x]+=val;
x-=lowbit(x);
}
}
int query(int x){
int res=0;
while(x<=cnt){
res+=c[x];
x+=lowbit(x);
}
return res;
}
}
int fa[N],len[N],tot=n;
void build(){
tot=5000;
int Sum=0;
for(int i=2;i<=5000;i++){
if(min_prime[i]==i){
fa[i]=1;
Sum++;
len[i]=Sum;
Fenwick_Tree::update(min_id[i],1);
continue;
}
int mx=0,Val=i,Cnt=0;
while(min_prime[Val]){
mx=max(mx,min_id[Val]);
Val/=min_prime[Val];
Cnt++;
}
int same=Fenwick_Tree::query(mx),dis=Sum-same,x=i-1;
while(dis>=len[x]){
dis-=len[x];
x=fa[x];
}
if(dis==0){
fa[i]=x;
Sum+=Cnt;
len[i]=Sum-same;
}
else{
int p=++tot;
fa[p]=fa[x];
len[p]=len[x]-dis;
fa[x]=p;
len[x]=dis;
Sum+=Cnt;
fa[i]=p;
len[i]=Sum-same;
}
Val=i;
while(min_prime[Val]){
Fenwick_Tree::update(min_id[Val],1);
Val/=min_prime[Val];
}
}
}
int siz[N],ans[N],mn=inf;
vector<int>e[N];
void dfs(int u,int fa,int dis){
siz[u]=val[u];
for(auto v:e[u]){
if(v==fa){
continue;
}
dfs(v,u,dis+len[v]);
siz[u]+=siz[v];
ans[1]+=siz[v]*len[v];
}
}
void get_rt(int u,int fa){
if(ans[u]<mn){
mn=ans[u];
}
for(auto v:e[u]){
if(v==fa){
continue;
}
ans[v]=ans[u]+len[v]*(n-siz[v]*2);
get_rt(v,u);
}
}
void solve(){
init(5000);
read(n);
for(int i=1,x;i<=n;i++){
read(x);
val[x]++;
}
val[1]+=val[0];
build();
for(int i=2;i<=tot;i++){
e[fa[i]].pb(i);
e[i].pb(fa[i]);
}
dfs(1,0,0);
get_rt(1,0);
write_endl(mn);
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
int t=1;
while(t--){
solve();
}
return 0;
}