Forethought Future Cup - Elimination Round
A.最长不会超过a的个数的两倍-1。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=100; 10 int n,d; 11 char s[N]; 12 13 int main(){ 14 scanf("%s",s+1); n=strlen(s+1); 15 rep(i,1,n) if (s[i]=='a') d++; 16 printf("%d\n",min(d*2-1,n)); 17 return 0; 18 }
B.删除所有a后一定是两个完全相同的串接起来。
1 #include<string> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 typedef long long ll; 8 using namespace std; 9 10 string s,t; 11 12 int main(){ 13 cin>>s; int l=s.length(); 14 rep(i,0,l-1) if (s[i]!='a') t+=s[i]; 15 if (t.length()&1){ puts(":("); return 0; } 16 int p=t.length()/2; 17 string t1=t.substr(0,p),t2=t.substr(p,p); 18 if (t1==t2 && t1==s.substr(s.length()-p,p)) cout<<s.substr(0,s.length()-p)<<endl; 19 else puts(":("); 20 return 0; 21 }
C.一种方法是直接二进制分组,能处理图的情况。
另一种是回顾一种树上找直径的方法,先任选一个点u找到离它最远的一个点v,再从v开始找到离它最远的点w,v到w就是直径。
这道题先任选u,然后通过二分点集找到最远点v,再一次查询即可得到直径。
1 #include<string> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 typedef long long ll; 8 using namespace std; 9 10 string s,t; 11 12 int main(){ 13 cin>>s; int l=s.length(); 14 rep(i,0,l-1) if (s[i]!='a') t+=s[i]; 15 if (t.length()&1){ puts(":("); return 0; } 16 int p=t.length()/2; 17 string t1=t.substr(0,p),t2=t.substr(p,p); 18 if (t1==t2 && t1==s.substr(s.length()-p,p)) cout<<s.substr(0,s.length()-p)<<endl; 19 else puts(":("); 20 return 0; 21 }
F.有多种做法,我的f[i][0/1/2]表示以i为根的子树中,所有叶子(包括根)都已经被确定分到某集合中/存在叶子未被划分集合(它们将和i以上的点配对)/所有叶子都已划分但根不属于任何一个集合,的方案数。转移显然,主要关注初始值的设定,叶子和非叶节点是不一样的。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 5 using namespace std; 6 7 const int N=200010,mod=998244353; 8 int n,cnt,fa[N],f[N][3],h[N],to[N],nxt[N]; 9 10 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 11 12 void dfs(int x){ 13 if (!h[x]){ f[x][1]=f[x][2]=1; return; } 14 f[x][2]=1; 15 For(i,x){ 16 dfs(k=to[i]); 17 f[x][0]=(1ll*f[x][0]*(f[k][0]+f[k][2])+1ll*f[x][1]*f[k][1])%mod; 18 f[x][1]=(1ll*(f[x][1]+f[x][2])*f[k][1]+1ll*f[x][1]*(f[k][0]+f[k][2]))%mod; 19 f[x][2]=1ll*f[x][2]*(f[k][0]+f[k][2])%mod; 20 } 21 } 22 23 int main(){ 24 scanf("%d",&n); 25 rep(i,2,n) scanf("%d",&fa[i]),add(fa[i],i); 26 dfs(1); printf("%d\n",(f[1][0]+f[1][2])%mod); 27 return 0; 28 }
G.每个建筑拆成h+1个点分别代表它的高度为[0,h],第i-1个点向第i个点连流量为h^2-i^2的边,第h个点向T连流量无穷的边。对于每个询问,将[l,r]中的代表高度h的点全部连向一个新点,流量无穷,再从新点向T连流量为c的边。n*h*h-最小割就是最终答案。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 5 using namespace std; 6 7 const int N=10010,inf=1e9; 8 int n,H,m,S,T,l,r,x,c,ans,q[N],cur[N],d[N],cnt=1,h[N],to[N],nxt[N],fl[N]; 9 10 int F(int i,int j){ return (i-1)*(H+1)+j+1; } 11 12 void add(int u,int v,int w){ 13 to[++cnt]=v; fl[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; 14 to[++cnt]=u; fl[cnt]=0; nxt[cnt]=h[v]; h[v]=cnt; 15 } 16 17 bool bfs(){ 18 rep(i,1,T+m) d[i]=0; d[S]=1; q[1]=S; 19 for (int st=0,ed=1; st!=ed; ){ 20 int x=q[++st]; 21 For(i,x) if (fl[i] && !d[k=to[i]]) 22 d[k]=d[x]+1,q[++ed]=k; 23 } 24 return d[T]; 25 } 26 27 int dfs(int x,int lim){ 28 if (x==T) return lim; 29 int c=0; 30 for (int &i=cur[x],k; i; i=nxt[i]) 31 if (fl[i] && d[k=to[i]]==d[x]+1){ 32 int t=dfs(k,min(lim-c,fl[i])); 33 c+=t; fl[i]-=t; fl[i^1]+=t; 34 if (c==lim) return c; 35 } 36 if (!c) d[x]=-1; 37 return c; 38 } 39 40 int main(){ 41 scanf("%d%d%d",&n,&H,&m); S=F(n,H)+1; T=S+1; 42 rep(i,1,n){ 43 add(S,F(i,0),H*H); 44 rep(j,1,H) add(F(i,j-1),F(i,j),H*H-j*j); 45 add(F(i,H),T,inf); 46 } 47 rep(i,1,m){ 48 scanf("%d%d%d%d",&l,&r,&x,&c); 49 rep(j,l,r) add(F(j,x),T+i,inf); 50 add(T+i,T,c); 51 } 52 while (bfs()){ 53 rep(i,1,T+m) cur[i]=h[i]; 54 ans+=dfs(S,inf); 55 } 56 printf("%d\n",n*H*H-ans); 57 return 0; 58 }
H.显然就是求五个点的凸包个数。一个方法是枚举上顶点,将所有点按相对于它的极角排序,再枚举第3,4个点,第2,5个点就是前缀后缀中满足某条件的点的个数,树状数组在线统计一下即可。$O(n^3\log n)$
另一个方法是f[i][j][k(0..5)]表示第一个点为i,当前确定了k条边,最后一个点是j,的方案数。将所有边按斜率排序,然后跑类似Bellman-Ford即可。$O(n^3)$
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=310; 9 int n,tot; 10 ll ans,f[N][N][6]; 11 struct P{ int x,y; }p[N]; 12 struct L{ P x; int a,b; }e[N*N]; 13 14 P operator -(const P &a,const P &b){ return (P){a.x-b.x,a.y-b.y}; } 15 P operator +(const P &a,const P &b){ return (P){a.x+b.x,a.y+b.y}; } 16 ll operator *(const P &a,const P &b){ return 1ll*a.x*b.y-1ll*b.x*a.y; } 17 bool operator <(const P &a,const P &b){ return a*b<0; } 18 bool operator <(const L &a,const L &b){ 19 if (a.x<b.x) return 1; 20 if (b.x<a.x) return 0; 21 return a.a==b.a ? a.b<b.b : a.a<b.a; 22 } 23 24 int main(){ 25 scanf("%d",&n); 26 rep(i,1,n) scanf("%d%d",&p[i].x,&p[i].y); 27 rep(i,1,n) rep(j,1,n) if (i!=j) e[++tot]=(L){p[j]-p[i],i,j}; 28 sort(e+1,e+tot+1); 29 rep(i,1,tot){ 30 int u=e[i].a,v=e[i].b; f[u][v][1]++; 31 rep(i,1,5) rep(j,1,n) f[j][v][i+1]+=f[j][u][i]; 32 } 33 rep(i,1,n) ans+=f[i][i][5]; 34 cout<<ans<<endl; 35 return 0; 36 }