Task1
这个是USACO 2019 JAN Gold的原题,可能因为过于水,所以我即使八点多才开始做也提前ak。。。来写一篇题解。。
A. Cow Poetry
显然押同一韵的行只需要最后一个词属于一个韵部,前面长度$K-s_i$随便排,DP一下长度$i$的有多少种,类似于背包转移,只是把物品放在内层,容量放在外层枚举,这样保证顺序不同时有不同的种类数。
然后对于每一种要求韵相同的行,可以枚举每一种韵,结合乘法原理和加法原理得出方案数。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define dbg(x) cerr << #x << " = " << x <<endl 7 using namespace std; 8 typedef long long ll; 9 typedef double db; 10 typedef pair<int,int> pii; 11 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 12 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 13 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 14 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 15 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 16 template<typename T>inline T read(T&x){ 17 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 18 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 19 } 20 const int N=5000+7,P=1e9+7; 21 char opt[3]; 22 int f[N],now; 23 int cnt[27],typ[N]; 24 int s[N],c[N]; 25 int n,m,k,maxc,ans=1; 26 inline void add(int&A,int B){A+=B;A>=P&&(A-=P);} 27 inline int fpow(int x,int p){int ret=1;for(;p;p>>=1,x=x*1ll*x%P)if(p&1)ret=ret*1ll*x%P;return ret;} 28 29 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 30 read(n),read(m),read(k); 31 for(register int i=1;i<=n;++i)read(s[i]),MAX(maxc,read(c[i])); 32 for(register int i=1;i<=m;++i)scanf("%s",opt),++cnt[opt[0]-'A']; 33 f[0]=1; 34 for(register int i=0;i<=k;++i) 35 for(register int j=1;j<=n;++j)if(i+s[j]<=k)add(f[i+s[j]],f[i]); 36 for(register int i=1;i<=n;++i)if(k>=s[i])add(typ[c[i]],f[k-s[i]]); 37 for(register int i=0;i<26;++i)if(cnt[i]){ 38 int tmp=0; 39 for(register int j=1;j<=maxc;++j)if(typ[j]){ 40 add(tmp,fpow(typ[j],cnt[i])); 41 } 42 ans=ans*1ll*tmp%P; 43 } 44 printf("%d ",ans); 45 return 0; 46 }
B. Sleepy Cow Sorting
这个真的水。。可以发现显然最后最长的有序上升序列不要动,把前面的数不断往后插,这样就保证了次数最少。于是BIT维护一发即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define dbg(x) cerr << #x << " = " << x <<endl 7 using namespace std; 8 typedef long long ll; 9 typedef double db; 10 typedef pair<int,int> pii; 11 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 12 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 13 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 14 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 15 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 16 template<typename T>inline T read(T&x){ 17 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 18 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 19 } 20 const int N=1e5+7; 21 int C[N]; 22 int a[N]; 23 int n,las,pos; 24 #define lowbit(x) x&(-x) 25 inline void add(int x){for(;x<=n;x+=lowbit(x))++C[x];} 26 inline int sum(int x){int ret=0;for(;x;x-=lowbit(x))ret+=C[x];return ret;} 27 28 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 29 read(n); 30 for(register int i=1;i<=n;++i)read(a[i]); 31 las=a[n];add(las);pos=n-1; 32 for(register int i=n-1;i;--i)if(MIN(las,a[i]))--pos,add(a[i]);else break; 33 printf("%d ",pos); 34 for(register int i=1;i<=pos;++i)printf("%d ",pos-i+sum(a[i])),add(a[i]); 35 puts("");return 0; 36 }
C. Shortcut
每个点到根的最短路路径是唯一的(因为字典序已经要求最小的),所以构成的最短路树就是唯一的,于是跑一遍dijkstra记录父边(注意更新min)之后在最短路树上暴力枚举把哪个和根相连,计算贡献取max即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #define dbg(x) cerr << #x << " = " << x <<endl 8 using namespace std; 9 typedef long long ll; 10 typedef double db; 11 typedef pair<int,int> pii; 12 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 13 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 14 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 15 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 16 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 17 template<typename T>inline T read(T&x){ 18 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 19 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 20 } 21 const int N=1e5+7; 22 struct thxorz{int to,nxt,w;}G[N]; 23 int Head[N],tot,c[N]; 24 int n,m,t; 25 inline void Addedge(int x,int y,int z){ 26 G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z; 27 G[++tot].to=x,G[tot].nxt=Head[y],Head[y]=tot,G[tot].w=z; 28 } 29 int dis[N],fa[N],val[N]; 30 priority_queue<pii,vector<pii>,greater<pii> > q; 31 #define y G[j].to 32 inline void dij(){ 33 memset(fa,0x3f,sizeof fa); 34 memset(dis,0x3f,sizeof dis);q.push(make_pair(dis[1]=0,1)); 35 while(!q.empty()){ 36 int d=q.top().first,x=q.top().second;q.pop(); 37 if(dis[x]^d)continue; 38 for(register int j=Head[x];j;j=G[j].nxt) 39 if(d+G[j].w==dis[y]&&MIN(fa[y],x))val[y]=G[j].w; 40 else if(d+G[j].w<dis[y])q.push(make_pair(dis[y]=d+G[j].w,y)),fa[y]=x,val[y]=G[j].w; 41 } 42 } 43 ll ans=0; 44 int dfs(int x,int fa,int dep){ 45 int siz=c[x]; 46 for(register int j=Head[x];j;j=G[j].nxt)if(y^fa)siz+=dfs(y,x,dep+G[j].w); 47 MAX(ans,(dep-t)*1ll*siz); 48 return siz; 49 } 50 #undef y 51 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 52 read(n),read(m),read(t); 53 for(register int i=1;i<=n;++i)read(c[i]); 54 for(register int i=1,x,y,z;i<=m;++i)read(x),read(y),read(z),Addedge(x,y,z); 55 dij();memset(Head,0,sizeof Head);tot=0; 56 for(register int i=2;i<=n;++i)Addedge(i,fa[i],val[i]); 57 dfs(1,0,0); 58 printf("%lld ",ans); 59 return 0; 60 }
Task2
A. Redistricting
简化题意:01序列划分块,每块最多$K$个,最小化:块内1个数大于等于0个数的块数。
列出dp式子
$f_i=min{f_j+[s_i-s_jge lfloorfrac{i-j+1}{2} floor]}$
其中$s_i$是G数量前缀和。
然后那个判断式只能是$0/1$,可以用单调队列维护一个$f$单调不减的序列,新元素小于队尾的肯定更优(因为最多相差1),当相等时讨论,如果这两个元素之间的1数量大于等于0数量,之前决策一定不优,因为选他只会增加1相对于0的数量。
然后用这两个条件维护队列即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define dbg(x) cerr << #x << " = " << x <<endl 7 using namespace std; 8 typedef long long ll; 9 typedef double db; 10 typedef pair<int,int> pii; 11 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 12 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 13 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 14 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 15 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 16 template<typename T>inline T read(T&x){ 17 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 18 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 19 } 20 const int N=3e5+7; 21 int a[N],f[N],q[N],n,k,l,r; 22 char s[N]; 23 24 int main(){//freopen("testdata.in","r",stdin);//freopen("test.ans","w",stdout); 25 read(n),read(k); 26 scanf("%s",s+1); 27 for(register int i=1;i<=n;++i)a[i]+=a[i-1]+(s[i]=='G'); 28 q[l=r=1]=0; 29 for(register int i=1;i<=n;++i){ 30 while(l<=r&&q[l]<i-k)++l; 31 f[i]=f[q[l]]+(a[i]-a[q[l]]>=i-q[l]+1>>1);//dbg(i),dbg(f[i]); 32 while(l<=r&&(f[q[r]]>f[i]||f[q[r]]==f[i]&&a[i]-a[q[r]]>=i-q[r]+1>>1))--r; 33 q[++r]=i; 34 } 35 printf("%d ",f[n]); 36 return 0; 37 }
B. Exercise
题意:就是说给一棵树,然后有一堆非树边,问这张图有多少简单环恰好包含两条非树边。
首先由一个非常神仙的结论,对于两条非树边,他们对应的路径必须有相交的边才可以在一个环内。
反正我是真的想不到。。然后还不知道为什么他就是对的,坑了几个小时,配合各种大力分类,感觉是可以理解一点了。。
比如说一条非树边对应两点$x,y$,另外一条非树边的一点$a$希望走到$x$、跳到$y$然后再走到$b$。一个显然的套路是把非树边转化为树链。
如果$a$在$x$子树内,$b$只要不同时在其子树内即可。$a$不在子树内,那么寻找一条树上路径$a o x$,然后跳到$y$。因为要求路径上的点不重合,所以$y$出发,不能走到之前走过的地方,也就是要“提前拐弯”走别的路,这样,$a o x o y o b$的路径在树链$x,y$上没有交,又发现,树链上有一小段路没有走过,画一下发现$a$出发到达这个地方直接走这段路后可以直接到$b$,也就是说,$a,b$和$x,y$的树链有相交部分,才可以有点不重复路径,换言之就是简单环。然后发现上述两种分类都成立。
所以问题就转化为若干个树链有多少对有相交部分。
先考虑序列上给若干个区间,显然做法是每一个区间统计它后面有多少区间和他相交(不然会算两次),也就是后面起点在$[L_i,R_i]$的区间个数,这个直接用前缀和就行了,注意:对于起点相同的区间,每个区间都会去统计另外几个,注意减去多余的。
放到树上,一个树链拆成两个直上直下的链,看有多少其他链至少和其中一个相交,方法类似,相当于从根向下的一条链上一个区间,有多少其他区间和他相交。这里注意几点:必须统计起点(指靠上的点)落在这个区间内部的区间个数,如果是结束点会有问题。。我一开始就是挂在这里的,有一种两条链中间相交然后分叉的反例。另外,也要注意两种重复的情况:一种是链起始边相同会造成重复统计,这个记录同一条边作为起始边个数,另一种是拆出来的两条链和问的两条链都有相交,这个用map塞pair统计解决。
并不是很容易说。。个人也没有很深入的理解。。具体还是要看代码。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 #define mst(x) memset(x,0,sizeof x) 9 #define dbg(x) cerr << #x << " = " << x <<endl 10 #define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl 11 using namespace std; 12 typedef long long ll; 13 typedef double db; 14 typedef pair<int,int> pii; 15 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 16 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 17 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 18 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 19 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 20 template<typename T>inline T read(T&x){ 21 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 22 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 23 } 24 const int N=2e5+7; 25 struct thxorz{ 26 int head[N],nxt[N<<1],to[N<<1],tot; 27 thxorz(){tot=1;} 28 inline void add(int x,int y){ 29 to[++tot]=y,nxt[tot]=head[x],head[x]=tot; 30 to[++tot]=x,nxt[tot]=head[y],head[y]=tot; 31 } 32 }G; 33 map<pii,int> mp; 34 int qx[N],qy[N],lca[N]; 35 int n,m,tmp; 36 ll ans; 37 #define y G.to[j] 38 int dep[N],son[N],sum[N],fa[N],topfa[N],st[N]; 39 void dfs0(int x,int fat){ 40 fa[x]=fat,dep[x]=dep[fat]+1,sum[x]=1;int tmp=-1; 41 for(register int j=G.head[x];j;j=G.nxt[j])if(y^fat)dfs0(y,x),sum[x]+=sum[y],MAX(tmp,sum[y])&&(son[x]=y); 42 } 43 void dfs1(int x,int topf){ 44 topfa[x]=topf;if(!son[x])return;dfs1(son[x],topf); 45 for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa[x]&&y^son[x])dfs1(y,y); 46 } 47 void dfs2(int x){for(register int j=G.head[x];j;j=G.nxt[j])if(y^fa[x])st[y]+=st[x],dfs2(y);} 48 #undef y 49 inline int LCA(int x,int y){ 50 while(topfa[x]^topfa[y])dep[topfa[x]]>dep[topfa[y]]?x=fa[topfa[x]]:y=fa[topfa[y]]; 51 return dep[x]<dep[y]?x:y; 52 } 53 inline int jump(int x,int lca){ 54 int las=0; 55 while(topfa[x]^topfa[lca])las=topfa[x],x=fa[las]; 56 return x==lca?las:son[lca]; 57 } 58 59 int main(){freopen("exercise.in","r",stdin);freopen("exercise.out","w",stdout); 60 read(n),m=read(m)-n+1; 61 for(register int i=1,x,y;i<n;++i)read(x),read(y),G.add(x,y); 62 dfs0(1,0),dfs1(1,1); 63 for(register int i=1,bx,by;i<=m;++i){ 64 read(qx[i]),read(qy[i]); 65 lca[i]=LCA(qx[i],qy[i]); 66 if(lca[i]^qx[i])bx=jump(qx[i],lca[i]),ans-=++st[bx]; 67 if(lca[i]^qy[i])by=jump(qy[i],lca[i]),ans-=++st[by]; 68 if(lca[i]^qx[i]&&lca[i]^qy[i]){ 69 if(bx>by)_swap(bx,by); 70 ans-=mp[make_pair(bx,by)]++; 71 } 72 } 73 dfs2(1); 74 for(register int i=1;i<=m;++i)ans+=st[qx[i]]+st[qy[i]]-(st[lca[i]]<<1); 75 printf("%lld ",ans); 76 return 0; 77 }
C. tracking2
神仙计数题,不会,自闭中。。。
To be continued.