T1 打地鼠
解题思路
数据范围比较小,不需要什么优化。
直接二维前缀和枚举右下角端点就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e3+10;
int n,m,ans,s[N][N],pre[N][N];
char ch[N];
signed main()
{
n=read(); m=read();
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
for(int j=1;j<=n;j++)
s[i][j]=ch[j]-'0';
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+s[i][j];
if(m>=n)
{
printf("%lld",pre[n][n]);
return 0;
}
for(int i=m;i<=n;i++)
for(int j=m;j<=n;j++)
ans=max(ans,pre[i][j]-pre[i-m][j]-pre[i][j-m]+pre[i-m][j-m]);
printf("%lld",ans);
return 0;
}
T2 竞赛图
解题思路
设 \(e(S)\) 表示点集 \(S\) 向点集以外的点的连边的交集为 \(e(S)\) 。
我们已经知道了每一个点向外面连边的点集了。
因此可以把大的集合拆成两部分对于两部分的 e 取交集。
这里为了方便直接取了 \(lowbit\) 。。。
因为保证了每两个点之间只有一条有向边,因此 \(S\) 向 \(e(S)\) 的任何一个子集的连边一定是单向的。
因此他们共同构成的一个点集不是强联通的,其他的就是合法的了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=25;
int T,n,ans,e[1<<N];
bool jud[1<<N];
int lowbit(int x){return x&(-x);}
void solve()
{
memset(e,0,sizeof(e));
memset(jud,true,sizeof(jud));
n=read(); ans=0;
e[0]=(1<<n)-1;
for(int i=1;i<=n;i++)
for(int j=1,x;j<=n;j++)
{
x=read();
e[1<<i-1]|=x<<j-1;
}
for(int i=1;i<(1<<n);i++)
e[i]=e[i^lowbit(i)]&e[lowbit(i)];
for(int i=1;i<(1<<n);i++)
if(jud[i])
for(int j=e[i];j;j=(j-1)&e[i])
jud[i|j]=false;
for(int i=0;i<(1<<n);i++)
ans+=jud[i];
printf("%lld\n",ans);
}
signed main()
{
T=read();
while(T--) solve();
return 0;
}
T3 糖果
解题思路
动态规划
设 \(f_{i,j,k}\) 表示当前小A选择到 i ,小B选择到 j ,小C还有 k 个备选位置。
如果 i 当前扫到的数在 a 中的优先级比 b 小,那么就一定是被小C 拿走了,先加到以后的里面去。
否则的话就相当于是小A选走了,那么此时的小C的备选位置就少了一个。
然后对于小B 的选择也是类似。
为了方便转移,我们再给 f 数组加一维\(0/1\) 表示当前进行到了那一步。
由于我太菜了,至今都没想到问什么后面要乘上一个 \(3\times i-1\)和 \(3\times i -2\)。(逃
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=410,M=150,mod=1e9+7;
int n,ans,f[N][N][M][2],a[N],b[N],pos1[N],pos2[N];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
pos1[a[i]]=i;
}
for(int i=1;i<=n;i++)
{
b[i]=read();
pos2[b[i]]=i;
}
f[1][1][0][0]=1;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=n+1;j++)
for(int k=0;k<=n/3;k++)
{
if(f[i][j][k][0])
{
if(i==n+1)
{
if(!k) ans=(ans+f[i][j][k][0])%mod;
continue;
}
if(pos2[a[i]]<j) (f[i+1][j][k][0]+=f[i][j][k][0])%=mod;
else
{
(f[i][j][k][1]+=f[i][j][k][0])%=mod;
if(k) (f[i+1][j][k-1][0]+=f[i][j][k][0]*k%mod)%=mod;
}
}
if(f[i][j][k][1])
{
if(pos1[b[j]]<i) (f[i][j+1][k][1]+=f[i][j][k][1])%=mod;
else if(a[i]!=b[j])
{
(f[i+1][j+1][k+1][0]+=f[i][j][k][1])%=mod;
if(k) (f[i][j+1][k-1][1]+=f[i][j][k][1]*k%mod)%=mod;
}
}
}
for(int i=1;i<=n;i++)
if(i%3)
ans=ans*i%mod;
printf("%lld",ans);
return 0;
}
T4 树
解题思路
几乎是今年 NOI 的原题吧(尽管我没有资格去考,也没有看过这个题)
在每一次操作的时候给每一个点涂上一种新的颜色。
那么两端颜色相同的就是白色边,反之就是黑色边。
然后就可以直接树链剖分+线段树维护当前区间的颜色种类数以及两端的颜色了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e5+10;
int n,q,col;
int tot,head[N],ver[N<<1],nxt[N<<1];
int tim,dep[N],siz[N],son[N],topp[N],fa[N],dfn[N],id[N];
struct Segment_Tree
{
int dat,laz,lc,rc;
}tre[N<<2];
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
id[tim]=x;
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
void push_up(int x)
{
tre[x].dat=tre[ls].dat+tre[rs].dat+(tre[ls].rc!=tre[rs].lc);
tre[x].lc=tre[ls].lc; tre[x].rc=tre[rs].rc;
}
void push_down(int x)
{
if(!tre[x].laz) return ;
tre[ls].dat=tre[rs].dat=0;
tre[ls].lc=tre[rs].lc=tre[ls].rc=tre[rs].rc=tre[x].laz;
tre[ls].laz=tre[rs].laz=tre[x].laz;
tre[x].laz=0;
}
void build(int x,int l,int r)
{
if(l==r)
{
tre[x].lc=tre[x].rc=++col;
tre[x].dat=0;
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(x);
}
int query(int x,int l,int r,int pos)
{
if(l==r) return tre[x].lc;
push_down(x);
int mid=(l+r)>>1,ans;
if(pos<=mid) ans=query(ls,l,mid,pos);
else ans=query(rs,mid+1,r,pos);
push_up(x);
return ans;
}
int query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tre[x].dat;
push_down(x);
int mid=(l+r)>>1,sum=0;
if(L<=mid) sum+=query(ls,l,mid,L,R);
if(R>mid) sum+=query(rs,mid+1,r,L,R);
sum+=(L<=mid&&R>mid&&tre[ls].rc!=tre[rs].lc);
push_up(x);
return sum;
}
void update(int x,int l,int r,int L,int R,int num)
{
if(L<=l&&r<=R)
{
tre[x].dat=0;
tre[x].lc=tre[x].rc=num;
tre[x].laz=num;
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,num);
if(R>mid) update(rs,mid+1,r,L,R,num);
push_up(x);
}
void update(int x,int y)
{
col++;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
update(1,1,tim,dfn[topp[x]],dfn[x],col);
x=fa[topp[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,1,tim,dfn[x],dfn[y],col);
}
int solve(int x,int y)
{
int sum=0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
sum+=query(1,1,tim,dfn[topp[x]],dfn[x]);
sum+=(query(1,1,tim,dfn[topp[x]])!=query(1,1,tim,dfn[fa[topp[x]]]));
x=fa[topp[x]];
}
if(dep[x]>dep[y]) swap(x,y);
sum+=query(1,1,tim,dfn[x],dfn[y]);
return sum;
}
signed main()
{
n=read();
for(int i=1,x,y;i<n;i++)
{
x=read(); y=read();
add_edge(x,y);
add_edge(y,x);
}
dfs1(1);
dfs2(1,1);
build(1,1,tim);
q=read();
while(q--)
{
int opt,x,y;
opt=read(); x=read(); y=read();
if(opt==1) update(x,y);
else if(x!=y) printf("%lld\n",solve(x,y));
else printf("0\n");
}
return 0;
}