省选联测 33
阿克了一车。除了我。
今天四个文件名貌似都是 Ubuntu 版本号。
张三
双指针。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int n,k,ans;
char s[1000010];
int main(){
scanf("%s%d",s+1,&k);n=strlen(s+1);
int l=1,r=0,cnt=0;
for(;r<=n;){
while(r<=n&&cnt<=k){
r++;
if(s[r]=='1')cnt++;
}
ans=max(ans,r-l);
while(cnt>k){
if(s[l]=='1')cnt--;
l++;
}
}
printf("%d\n",ans);
return 0;
}
李四
状压,设 \(dp_S\) 为还差 \(S\) 内的节点没有入住,那么转移考虑枚举最后一个入住的在哪里定居。这个节点满足要么可以登陆,要么和另一个没人的节点相邻。这样我们显然可以安排一个顺序使得全部都可以入住。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int n,m,k,s[25];
bool v[25];
unsigned int dp[1<<23];
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
int x;scanf("%d",&x);v[x]=true;
}
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
s[u]|=1<<v-1;s[v]|=1<<u-1;
}
dp[(1<<n)-1]=1;
for(int i=(1<<n)-1;i>=0;i--){
for(int j=1;j<=n;j++){
if(((i>>(j-1))&1)&&(v[j]||(s[j]&(((1<<n)-1)^i))))dp[i^(1<<j-1)]+=dp[i];
}
}
cout<<dp[0]<<endl;
return 0;
}
王五
赛后问了一下切了这题的,一堆解法。几个人写的标算(不知道是标算还是暴力哈希过的),差不多的人数写的 AC 自动机,还有一个写了 SAM+线段树,然后写 SA 的也是就我一个。
首先把所有串拼起来中间加分隔符,后缀排序之后对于每个串就可以在 sa 上二分找到包含这个串的后缀的左右端点。这样就是个区间众数了。我写的分块,赛时忘了还有莫队这种东西。
虽然 4.4k 但是基本都是板子叠起来,挺好写的,一遍就写对了。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
int n,num;
char t[200010];
int s[200010],sa[200010],rk[200010],rk2[200010],key[200010],cnt[200010],bel[200010];
int len[200010],ht[200010],pos[200010],id[200010];
void getsa(){
int m=5;
for(int i=1;i<=n;i++){
rk[i]=s[i];cnt[rk[i]]++;
}
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
for(int w=1;;w<<=1){
int p=0;
for(int i=n;i>n-w;i--)id[++p]=i;
for(int i=1;i<=n;i++){
if(sa[i]>w)id[++p]=sa[i]-w;
}
memset(cnt+1,0,m*4);
for(int i=1;i<=n;i++){
key[i]=rk[id[i]];cnt[key[i]]++;
}
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[key[i]]--]=id[i];
memcpy(rk2+1,rk+1,n*4);
p=0;
for(int i=1;i<=n;i++){
rk[sa[i]]=(rk2[sa[i]]==rk2[sa[i-1]]&&rk2[sa[i]+w]==rk2[sa[i-1]+w])?p:++p;
}
if(p==n){
for(int i=1;i<=n;i++)sa[rk[i]]=i;
break;
}
m=p;
}
}
void getht(){
for(int i=1,k=0;i<=n;i++){
if(rk[i]==0)continue;
if(k)k--;
while(s[i+k]==s[sa[rk[i]-1]+k])k++;
ht[rk[i]]=k;
}
}
struct ST{
int st[200010][21];
void build(){
for(int i=1;i<=n;i++)st[i][0]=ht[i];
for(int j=1;j<=__lg(n);j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
int lcp(int l,int r){
if(l==r)return n-l+1;
l++;int k=__lg(r-l+1);
return min(st[l][k],st[r-(1<<k)+1][k]);
}
}st;
int getl(int id){
int l=1,r=pos[id];
while(l<r){
int mid=(l+r)>>1;
if(st.lcp(mid,pos[id])<len[id])l=mid+1;
else r=mid;
}
return l;
}
int getr(int id){
int l=pos[id],r=n;
while(l<r){
int mid=(l+r+1)>>1;
if(st.lcp(pos[id],mid)<len[id])r=mid-1;
else l=mid;
}
return l;
}
namespace block{
int sq,blk;
int L[510],R[510],belong[200010],a[200010];
int cnt[510][200010],f[510][510];
void build(){
for(int i=1;i<=n;i++)a[i]=bel[sa[i]];
sq=sqrt(n);blk=n/sq;
for(int i=1;i<=blk;i++){
L[i]=R[i-1]+1;
R[i]=L[i]+sq-1;
}
R[blk]=n;
for(int i=1;i<=blk;i++){
for(int j=L[i];j<=R[i];j++)belong[j]=i;
}
for(int i=1;i<=blk;i++){
for(int j=1;j<=num;j++)cnt[i][j]=cnt[i-1][j];
for(int j=L[i];j<=R[i];j++)if(a[j])cnt[i][a[j]]++;
}
for(int i=1;i<=blk;i++){
for(int j=1;j<=num;j++)f[i][i]=max(f[i][i],cnt[i][j]-cnt[i-1][j]);
for(int j=i+1;j<=blk;j++){
f[i][j]=f[i][j-1];
for(int k=L[j];k<=R[j];k++)f[i][j]=max(f[i][j],cnt[j][a[k]]-cnt[i-1][a[k]]);
}
}
}
int tmp[200010];
int query(int l,int r){
int ans=0;
if(belong[l]==belong[r]){
for(int i=l;i<=r;i++){
if(a[i])tmp[a[i]]++,ans=max(ans,tmp[a[i]]);
}
for(int i=l;i<=r;i++)if(a[i])tmp[a[i]]=0;
return ans;
}
ans=f[belong[l]+1][belong[r]-1];
for(int i=l;i<=R[belong[l]];i++){
if(a[i])tmp[a[i]]++,ans=max(ans,cnt[belong[r]-1][a[i]]-cnt[belong[l]][a[i]]+tmp[a[i]]);
}
for(int i=L[belong[r]];i<=r;i++){
if(a[i])tmp[a[i]]++,ans=max(ans,cnt[belong[r]-1][a[i]]-cnt[belong[l]][a[i]]+tmp[a[i]]);
}
for(int i=l;i<=R[belong[l]];i++)if(a[i])tmp[a[i]]=0;
for(int i=L[belong[r]];i<=r;i++)if(a[i])tmp[a[i]]=0;
return ans;
}
}
int main(){
scanf("%d",&num);
for(int i=1;i<=num;i++){
scanf("%s",t+1);len[i]=strlen(t+1);
pos[i]=n+1;
for(int j=1;j<=len[i];j++){
if(t[j]=='A')s[++n]=1;
else if(t[j]=='T')s[++n]=2;
else if(t[j]=='C')s[++n]=3;
else s[++n]=4;
bel[n]=i;
}
n++;
}
getsa();getht();
st.build();block::build();
for(int i=1;i<=num;i++)pos[i]=rk[pos[i]];
for(int i=1;i<=num;i++){
int l=getl(i),r=getr(i);
printf("%d\n",block::query(l,r));
}
return 0;
}
赵六
这玩意就是把最短路 DAG 跑出来之后求每个点的支配边个数。那么支配树裸题。
稍微说一下 DAG 上的支配树怎么跑的。按拓扑序求,每次对于点 \(x\) 找到所有能直接到达 \(x\) 的所有节点(就是连边到 \(x\) 的点)在支配树上的 lca 作为 \(x\) 在支配树上的父亲。这是求支配点,支配边就直接把边也当成点一块跑就行了。好像有更高级点的方法。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
int n,m;
struct node{
int v,w,next;
}edge[1000010];
int t,cnt,head[800010],ind[800010];
void add(int u,int v,int w=0){
edge[++t].v=v;edge[t].w=w;edge[t].next=head[u];head[u]=t;
}
long long dis[300010];
bool v[300010];
struct stu{
int x;long long w;
bool operator<(const stu &s)const{
return w>s.w;
}
};
vector<int>pre[300010];
void dijkstra(int st){
priority_queue<stu>q;
memset(dis,0x3f,sizeof(dis));
dis[st]=0;
q.push({st,0});
while(!q.empty()){
int x=q.top().x;q.pop();
if(!v[x]){
v[x]=true;
for(int i=head[x];i;i=edge[i].next){
if(dis[edge[i].v]>dis[x]+edge[i].w){
dis[edge[i].v]=dis[x]+edge[i].w;
pre[edge[i].v].clear();
pre[edge[i].v].push_back(x);
q.push({edge[i].v,dis[edge[i].v]});
}
else if(dis[edge[i].v]==dis[x]+edge[i].w){
pre[edge[i].v].push_back(x);
}
}
}
}
t=0;cnt=n;
for(int i=1;i<=n;i++)head[i]=0;
for(int i=1;i<=n;i++){
for(int f:pre[i]){
cnt++;add(f,cnt);add(cnt,i);
ind[cnt]++;ind[i]++;
}
}
}
int fa[800010][21],lc[800010],dep[800010],val[800010];
int lca(int x,int y){
if(dep[x]>dep[y])swap(x,y);
for(int i=__lg(cnt);i>=0;i--)if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
if(x==y)return x;
for(int i=__lg(cnt);i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void build(){
queue<int>q;
q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
dep[x]=dep[lc[x]]+1;fa[x][0]=lc[x];
for(int i=1;i<=__lg(cnt);i++)fa[x][i]=fa[fa[x][i-1]][i-1];
val[x]=val[lc[x]];
if(x>n)val[x]++;
for(int i=head[x];i;i=edge[i].next){
ind[edge[i].v]--;
if(!lc[edge[i].v])lc[edge[i].v]=x;
else lc[edge[i].v]=lca(lc[edge[i].v],x);
if(!ind[edge[i].v])q.push(edge[i].v);
}
}
}
int read(){
int x=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x;
}
int main(){
n=read();m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
add(u,v,w);add(v,u,w);
}
dijkstra(1);
build();
for(int i=1;i<=n;i++)printf("%d\n",val[i]);
return 0;
}
快踩