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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
方法二

 

posted @ 2019-04-29 19:03  HocRiser  阅读(176)  评论(0编辑  收藏  举报