20190922 模拟

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\n",ans);
45     return 0;
46 }
View Code

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\n",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 }
View Code

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\n",ans);
59     return 0;
60 }
View Code

Task2

A. Redistricting

简化题意:01序列划分块,每块最多$K$个,最小化:块内1个数大于等于0个数的块数。

列出dp式子

$f_i=\min\{f_j+[s_i-s_j\ge \lfloor\frac{i-j+1}{2}\rfloor]\}$

其中$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\n",f[n]);
36     return 0;
37 }
View Code

B. Exercise

题意:就是说给一棵树,然后有一堆非树边,问这张图有多少简单环恰好包含两条非树边。

首先由一个非常神仙的结论,对于两条非树边,他们对应的路径必须有相交的边才可以在一个环内。

反正我是真的想不到。。然后还不知道为什么他就是对的,坑了几个小时,配合各种大力分类,感觉是可以理解一点了。。

比如说一条非树边对应两点$x,y$,另外一条非树边的一点$a$希望走到$x$、跳到$y$然后再走到$b$。一个显然的套路是把非树边转化为树链。

如果$a$在$x$子树内,$b$只要不同时在其子树内即可。$a$不在子树内,那么寻找一条树上路径$a\to x$,然后跳到$y$。因为要求路径上的点不重合,所以$y$出发,不能走到之前走过的地方,也就是要“提前拐弯”走别的路,这样,$a\to x\to y\to 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\n",ans);
76     return 0;
77 }
View Code

C. tracking2

神仙计数题,不会,自闭中。。。

To be continued.

posted @ 2019-09-22 10:56  Ametsuji_akiya  阅读(193)  评论(0编辑  收藏  举报