程序自动分析
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],*iS,*iT;
char Get() { return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++); }
int read() {int x=0; char ch=Get(); while(ch>'9'||ch<'0') ch=Get(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=Get(); return x; }
}
using namespace IO;
const int N=1000007;
int fa[N<<1],t[N<<1];
struct node{int opt,u,v;}a[N];
int operator<(node a,node b){return a.opt>b.opt;}
int Find(int x){return x==fa[x]? x:fa[x]=Find(fa[x]);}
int main()
{
int T=read(),n,m,i,flg,fu,fv;
while(T--)
{
n=read(),m=flg=0,memset(t,0,sizeof t);
for(i=1;i<N;++i) fa[i]=i;
for(i=1;i<=n;++i) t[++m]=a[i].u=read(),t[++m]=a[i].v=read(),a[i].opt=read();
sort(t+1,t+m+1),sort(a+1,a+n+1),m=unique(t+1,t+m+1)-(t+1);
for(i=1;i<=n;++i) a[i].u=lower_bound(t+1,t+m+1,a[i].u)-t,a[i].v=lower_bound(t+1,t+m+1,a[i].v)-t;
for(i=1;i<=n;++i)
{
fu=Find(a[i].u),fv=Find(a[i].v);
if(a[i].opt) fa[fu]=fv;
else if(fu==fv) {flg=1,puts("NO");break;}
}
if(!flg) puts("YES");
}
return 0;
}
软件包管理器
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=200005;
int n,k=0,x,head[maxn],q,deep[maxn],father[maxn],size[maxn];
int tid[maxn],top[maxn],son[maxn],tidnum=0,pos[maxn];char s[15];
struct node
{
int to,next;
} edge[maxn<<1];
struct Node
{
int left,right,flag,sum;
} tree[maxn<<2];
void add(int u,int v)
{
edge[++k].to=v;
edge[k].next=head[u];
head[u]=k;
}
int read()
{
int x=0;char ch=getchar();
while(ch<48||ch>57) ch=getchar();
while(ch>=48&&ch<=57) x=x*10+ch-48,ch=getchar();
return x;
}
void dfs1(int x,int fa,int depth)
{
size[x]=1;father[x]=fa;deep[x]=depth;
for(int i=head[x];i;i=edge[i].next)
{
if(edge[i].to==fa) continue;
dfs1(edge[i].to,x,depth+1);
size[x]+=size[edge[i].to];
if(!son[x]||size[edge[i].to]>size[son[x]]) son[x]=edge[i].to;
}
}
void dfs2(int x,int tp)
{
tid[x]=++tidnum;pos[tid[x]]=x;top[x]=tp;
if(!son[x]) return;dfs2(son[x],tp);
for(int i=head[x];i;i=edge[i].next)
{
if(edge[i].to!=son[x]&&edge[i].to!=father[x])
dfs2(edge[i].to,edge[i].to);
}
}
void build(int id,int l,int r)
{
tree[id].left=l;tree[id].right=r;
tree[id].sum=0;tree[id].flag=-1;
if(l==r) return;
int mid=(l+r)>>1;
build(id<<1,l,mid);build(id<<1|1,mid+1,r);
return;
}
void downdata(int id)
{
tree[id<<1].sum=(tree[id<<1].right-tree[id<<1].left+1)*tree[id].flag;
tree[id<<1|1].sum=(tree[id<<1|1].right-tree[id<<1|1].left+1)*tree[id].flag;
tree[id<<1].flag=tree[id<<1|1].flag=tree[id].flag;
tree[id].flag=-1;
}
int get(int id,int l,int r)
{
if(tree[id].right<l||tree[id].left>r) return 0;
if(tree[id].right<=r&&tree[id].left>=l) return tree[id].sum;
if(tree[id].flag!=-1) downdata(id);
return get(id<<1,l,r)+get(id<<1|1,l,r);
}
void update(int id,int l,int r,int val)
{
if(tree[id].right<l||tree[id].left>r) return;
if(tree[id].right<=r&&tree[id].left>=l)
{
tree[id].sum=(tree[id].right-tree[id].left+1)*val;
tree[id].flag=val;
return;
}
if(tree[id].flag!=-1) downdata(id);
update(id<<1,l,r,val);update(id<<1|1,l,r,val);
tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
return;
}
void change(int u,int v,int val)
{
while(top[u]!=top[v])
{
if(deep[top[u]]<deep[top[v]]) std::swap(u,v);
update(1,tid[top[u]],tid[u],val);
u=father[top[u]];
}
if(deep[u]>deep[v]) std::swap(u,v);
update(1,tid[u],tid[v],val);
return;
}
int main()
{
n=read();
for(int i=2;i<=n;i++)
{
x=read();x++;
add(x,i);
}
dfs1(1,1,1);dfs2(1,1);
q=read();build(1,1,tidnum);
for(int i=1;i<=q;i++)
{
scanf("%s",s);
x=read();x++;
int t1=tree[1].sum;
if(s[0]=='i')
{
change(1,x,1);
int t2=tree[1].sum;
printf("%d\n",abs(t2-t1));
}
if(s[0]=='u')
{
update(1,tid[x],tid[x]+size[x]-1,0);
int t2=tree[1].sum;
printf("%d\n",abs(t1-t2));
}
}
return 0;
}
寿司晚宴
Luogu
LOJ
UOJ
BZOJ
我们知道\(>\sqrt n\)的质数的指数最多为\(1\),而\(\le\sqrt n\)的质数最多只有\(8\)个。
因此我们考虑将前\(8\)个质数状压,对每个数求出前\(8\)个质数中有哪些是它的因子。
设\(f_{s,t}\)表示第一/二个人选的数包含了\(s/t\)中的质数作为因子,那么显然\(ans=\sum\limits_{s\cap t=\varnothing}f_{s,t}\)。
考虑如何转移,我们先枚举一个质数,再对所有包含这个质数作为因子的数进行转移,为了保证每个数只进行一次转移我们不妨在它的最大质因子处进行转移。
对于一个数,它可能被两个人中的一个选,也有可能不被任何人选,并且两个人的转移是独立的。
因此我们先将\(f\)拷贝至\(g,h\),对两个人分别进行转移,最后再进行合并。
假设现在进行转移的数是\(x\),它的质因数集合(只考虑前\(8\)个质数)为\(y\),那么此时有转移:\(g_{s,t}\rightarrow g_{s\cup y,t},h_{t,s}\rightarrow h_{t,s\cup y}(t\cap y=\varnothing)\)。
最后合并就是\(f_{s,t}\leftarrow g_{s,t}+h_{s,t}-f_{s,t}\),减去转移之前的\(f_{s,t}\)是因为两人都没有选任何数不选的情况会被算重。
时间复杂度为\(O(3^nn)\)。
#include<cstdio>
const int N=507,M=257,pr[]={2,3,5,7,11,13,17,19};
int p,fac[N],mx[N],st[N],f[M][M],g[M][M],h[M][M];
void inc(int&a,int b){a+=b-p,a+=a>>31&p;}
int main()
{
int n,cnt=0,ans=0;scanf("%d%d",&n,&p),f[0][0]=1;
for(int i=0,j;i<8&&pr[i]<=n;++i) for(j=fac[++cnt]=pr[i];j<=n;j+=pr[i]) st[j]|=1<<i,mx[j]=pr[i];
for(int i=23,j;i<=n;++i) if(!mx[i]) for(j=fac[++cnt]=i;j<=n;j+=i) mx[j]=i;
for(int i=1;i<=cnt;++i)
{
for(int s=255;~s;--s) for(int t=s;t<256;t=(t+1)|s) h[s][t^s]=g[s][t^s]=f[s][t^s];
for(int x=fac[i];x<=n;x+=fac[i])
if(mx[x]==fac[i])
for(int s=255;~s;--s)
for(int t=s;t<256;t=(t+1)|s)
if(!((t^s)&st[x]))
inc(g[s|st[x]][t^s],g[s][t^s]),inc(h[t^s][s|st[x]],h[t^s][s]);
for(int s=255;~s;--s) for(int t=s;t<256;t=(t+1)|s) f[s][t^s]=(1ll*p+g[s][t^s]+h[s][t^s]-f[s][t^s])%p;
}
for(int s=255;~s;--s) for(int t=s;t<256;t=(t+1)|s) inc(ans,f[s][t^s]);
printf("%d",ans);
}
荷马史诗
Luogu
LOJ
UOJ
BZOJ
哈夫曼树的每个叶子结点都有一个权值(表示某数据的出现频率),且\(\sum dis_ival_i\)最小。
哈夫曼树中,权值和越大的集合离根节点越近。
而每个数据对应从根节点到该叶子结点的一种编码,显然这些编码没有相互的前缀和后缀关系。
对于文章压缩而言,叶子节点的权值就是某单词的出现次数,该节点到叶子结点的路径就是该叶子节点的单词压缩后的形态。
哈夫曼树的每个非叶子节点最多有\(k\)个儿子,在文章压缩中这个\(k\)就是字符集大小。
构建哈夫曼树的方法是每次选出未被选过的权值最小的\(k\)个点,这\(k\)个点的父亲的权值为这\(k\)个点的权值和。
这个构建方法显然可以用一个堆来做。
然后我们考虑怎么做这道题。
我们需要把节点个数补到\(k-1\)的倍数,来保证最后只有一个未被标记的点即根节点。
然后我们直接做就完事了。
最终的文章长度为\(\sum dis_ival_i\),也就是除根节点以外所有点的权值。
而最长字符串的长度就是最大节点深度-1。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=100007;
int n,k;
struct node{LL w;int h;};
int operator<(node a,node b){return a.w>b.w||(a.w==b.w&&a.h>b.h);}
priority_queue<node>q;
LL read(){LL x=0;char c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int main()
{
n=read(),k=read();int i,d;LL s,ans=0;
for(i=1;i<=n;++i) q.push((node){read(),1});
while((n-1)%(k-1)) q.push((node){0,1}),++n;
while(n^1)
{
s=d=0;
for(i=1;i<=k;++i) s+=q.top().w,d=max(d,q.top().h),q.pop();
ans+=s,q.push((node){s,d+1}),n-=(k-1);
}
printf("%lld\n%d",ans,q.top().h-1);
}
品酒大会
Luogu
LOJ
UOJ
BZOJ
从小到大枚举\(r\),\(height\)数组中每个极长连续的\(\ge r\)的子段中任意两个后缀都可以产生贡献。
当\(r\rightarrow r+1\)时有一些区间会合并,总合并次数是\(O(n)\)的。
记录各个极长连续子段的大小、最大值、最小值即可。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int read(){int x;scanf("%d",&x);return x;}
const int N=3e5+7;
int a[N],sa[N],rnk[N],tp[N],t[N],h[N],v[N],S,n,m,size[N],mx[N],mn[N],st[N],top;
char s[N];
LL ans[N],Ans[N];
void rsort()
{
for(int i=0;i<=S;++i) t[i]=0;
for(int i=1;i<=n;++i) ++t[rnk[i]];
for(int i=1;i<=S;++i) t[i]+=t[i-1];
for(int i=n;i;--t[rnk[tp[i]]],--i) sa[t[rnk[tp[i]]]]=tp[i];
}
void ssort()
{
int i,l,p;
for(i=1;i<=n;++i) a[i]=s[i-1]-'a'+1,rnk[i]=a[i],tp[i]=i;
S=26,rsort();
for(l=1,p=0;p<n;S=p,l<<=1)
{
for(p=0,i=n-l+1;i<=n;++i) tp[++p]=i;
for(i=1;i<=n;++i) if(sa[i]>l) tp[++p]=sa[i]-l;
rsort(),swap(tp,rnk),rnk[sa[1]]=p=1;
for(i=2;i<=n;++i) rnk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+l]==tp[sa[i]+l])? p:++p;
}
}
void geth()
{
int lcp=0,i,j;
for(i=1;i<=n;++i)
{
if(lcp) --lcp;
j=sa[rnk[i]-1];
while(i+lcp<=n&&j+lcp<=n&&a[i+lcp]==a[j+lcp]) ++lcp;
h[rnk[i]]=lcp;
}
}
void calc()
{
int i,Mx,Mn,Size;
for(i=1;i<=n;++i) Ans[i]=LLONG_MIN;
for(i=2;i<=n;++i)
{
Size=1,Mx=Mn=v[sa[i-1]];
while(top&&st[top]>=h[i]) ans[st[top]]+=1ll*size[top]*Size,Ans[st[top]]=max(Ans[st[top]],max(1ll*mx[top]*Mx,1ll*mn[top]*Mn)),Size+=size[top],Mx=max(Mx,mx[top]),Mn=min(Mn,mn[top]),--top;
st[++top]=h[i],size[top]=Size,mn[top]=Mn,mx[top]=Mx;
}
Size=1,Mx=Mn=v[sa[n]];
for(i=top;i;--i) ans[st[i]]+=1ll*size[i]*Size,Ans[st[i]]=max(Ans[st[i]],max(1ll*mx[i]*Mx,1ll*mn[i]*Mn)),Size+=size[i],Mx=max(Mx,mx[i]),Mn=min(Mn,mn[i]);
}
int main()
{
n=read(),scanf("%s",s);int i;
for(i=1;i<=n;++i) v[i]=read();
ssort(),geth(),calc();
for(i=n-2;~i;--i) ans[i]+=ans[i+1],Ans[i]=max(Ans[i],Ans[i+1]);
for(i=0;i<n;++i) printf("%lld %lld\n",ans[i],(ans[i]==0? 0:Ans[i]));
}