首页 > 学院 > 开发设计 > 正文

玲珑杯”ACM比赛 Round #11【待补】

2019-11-06 06:06:46
字体:
来源:转载
供稿:网友

题目:http://www.ifrog.cc/acm/contest/1013 官方题解:http://www.ifrog.cc/acm/solution/16

直接贴上了,没过的题以后再补(尽管不太可能QAQ)

第一题、直接枚举所有可能的x并且计算答案,得出最优解 【水题,忽略】

第二题、可以连接的两个工厂相当于可以匹配的两个点,那么问题转化为求两个串的最长公共子序列,但O(n^2)的复杂度会超时,由于第二个串每个点最多只有6个点与之匹配,所以可以把第二个串的每个点变成可以与之匹配的六个编号从大到小排序,然后求最长上升子序列。

【这种套路题还是见过些的】

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int N=1e6+9;const int INF=1e9+7;int g[N],d[N],a[N],k[11];int n;int main() { //freopen("in.txt","r",stdin); while(~scanf("%d",&n)) { int ans=0,cnt=0; for(int i=1; i<=n; i++)g[i]=INF; for(int i=0; i<n; i++) { for(int j=0; j<6; j++) { scanf("%d",&k[j]); } sort(k,k+6); for(int j=5;j>=0;j--)a[cnt++]=k[j]; } for(int i=1;i<=cnt;i++)g[i]=INF; for(int i=0;i<cnt;i++){ int k=lower_bound(g+1,g+n+1,a[i])-g; d[i]=k; g[k]=a[i]; ans=max(ans,d[i]); } cout<<ans<<endl; } return 0;}

第三题、当一课排序二叉树建立起来之后我们可以发现,从某个点x往上走,第一个往左的节点的值是在x前插入的小于x的最大值,第一个往右的值是在x前插入的大于x的最小值,所以x所在的层数就是在插入x之前比x大的最小值的层数和比x小的最大值的层数中,较大值加一,若把问题反过来看,不是插入而是从二叉树中把一个个点拿走,那么可以很简单地用并查集找到上述的两个值。

【讲真,这题挺不错的(虽然没做出来),还是可以做的】


第四题、由于x经过一次函数调用f(x)之后不超过9^3,所以直接找循环节即可。 【水题略过】


第五题、先对原串st做一次kmp,然后做动态规划f[i][j]表示长度为i的字符串中不存在子串为st并且所有后缀中与st的前缀匹配的最长长度为j,然后枚举第i+1位的字符,如果等于st[j+1]的话那么转移到f[i+1][j+1],否则转移到f[i][k],其中k由kmp算法计算得到。

【这题自己一开始想错了,疯狂WA,没做出来QAQ】

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int mod=1e9+7;const int N=1000+7;char ch, s[N];int dp[N][N], c[N][156], nex[N];int main() { int n, m, i, j, p, q, ans; //freopen("in.txt","r",stdin); while(~scanf("%d%d", &n, &m)) { scanf("%s",s+1); memset(dp, 0, sizeof(dp)); p = 0, q = 1, nex[1] = 0; while(q<=m) { if(p==0 || s[p]==s[q]) { nex[++q] = ++p; } else p = nex[p]; } for(i=1; i<=m; i++) { for(ch='a'; ch<='z'; ch++) { int p = i; while(p!=0 && s[p]!=ch) p = nex[p]; c[i][ch] = p; //PRintf("%d ",c[i][ch]); } // printf("/n"); } dp[0][0] = 1; for(i=1; i<=n; i++) { for(j=1; j<=m; j++) { for(ch='a'; ch<='z'; ch++) dp[i][c[j][ch]] = (dp[i][c[j][ch]]+dp[i-1][j-1])%mod; } } ans = 0; for(i=0; i<=m-1; i++) ans = (ans+dp[n][i])%mod; printf("%d/n", ans); } return 0;}

第六题、只要判断两个:1、所有对角线连接的端点颜色都不一样;2、所有线段都不想交。

【哇~~,题解好简单啊(一开始没看到相邻两点颜色不同,还想了好一会儿 T_T)】


第七题、我们先计算出tornado的速度为v0,然后分类讨论:1、如果v > v0那么肯定可以追上;2、如果v == v0,那么速度向量与(x1-x0, y1-y0)的夹角小于90度就可以追上;3、我们先假设可以追上,而且时间为t,那么追上时tornado走过的路程长度为t v0,Feigay走过的路程为t v,加上tornado和Feigay原始位置的距离,还有速度向量和(x1-x0, y1-y0)的夹角可以根据余弦定理列出方程,若有正数解那么可以追上。

【题解讲的很清楚了,分情况讨论即可】


第八题、可以先想到一个dp的方法f[i][j] = max{f[i-1][k] + A[i] + B[j] | k < j 且(i, j)可以匹配, f[i-1][j], f[i][j-1], f[i-1][j-1]}。可是这个dp是O(n^2)时间复杂度的,我们可以观察转移方程,发现求最大值的k其实是一段连续的值,所以可以利用选段树或者树状数组进行优化,把复杂度降低成O(m * log n)。

【没想过dp,一上来直接考虑树状数组啊】

#include<bits/stdc++.h>using namespace std;#define fi first#define se secondtypedef pair<int,int>pii;typedef long long ll;const int INF=0x3f3f3f3f;const int N=3000000+7;ll c[N],a[N],b[N];pii e[N];int cmp(const pii& a,const pii& b){ if(a.fi==b.fi)return a.se>b.se; return a.fi<b.fi;}int lowbit(int x){return x&(-x);}void add(int x,ll v){ for(int i=x;i<N;i+=lowbit(i))c[i]=max(c[i],v);}ll query(int x){ ll maxn=0; for(int i=x;i>0;i-=lowbit(i))maxn=max(maxn,c[i]); return maxn;}int main(){ //freopen("in.txt","r",stdin); int n,m; while(~scanf("%d%d",&n,&m)){ memset(c,0,sizeof(c)); for(int i=0;i<m;i++){ scanf("%d%d",&e[i].fi,&e[i].se); } for(int i=1;i<=n;i++)scanf("%lld",&a[i]); for(int i=1;i<=n;i++)scanf("%lld",&b[i]); sort(e,e+m,cmp); for(int i=0;i<m;i++){ int u=e[i].fi,v=e[i].se; ll t=query(v-1); add(v,t+a[u]+b[v]); } printf("%lld/n",query(n)); } return 0;}

第九题、如果n大于6的时候结果肯定为0,因为每次a与b操作之后至少可以使a的1的数量减少一半,所以进行6次操作肯定可以使a变成0。当n小于等于6的时候,暴力枚举每个数是否取反以及每个数之间的符号。

【看完题解恍然大悟啊QAQ,因为a和b或者~b相与,至少可以使a的数量减少一半,那么进行6次以上肯定是0了。相与6直接暴力。另外,其实或,异或没啥用(或是真的没用,异或可以用~和&代替】

#include<bits/stdc++.h>using namespace std;typedef unsigned long long ll;const int N=1e6+9;const int INF=1e9+7;ll a[N], ans;int n;void dfs(int x, ll now) { if (x == n) { ans = min(ans, now); return; } dfs(x+1, now & a[x]); dfs(x+1, now & ~a[x]);}int main() { while (~scanf("%d", &n)) { for (int i = 0; i < n; ++i) { scanf("%llu", &a[i]); } if (n > 6) { printf("0/n"); continue; } else { ans = ~(0ll); dfs(1, ~a[0]); dfs(1, a[0]); printf("%llu/n", ans); } } return 0;}

第十题、先求出树的dfs序,进入一个点以及离开一个点都在dfs序上有一个标记,然后每一条链和子树上的操作都可以变成dfs序上的一段区间,把dfs序做成两棵线段树,黑白各一棵,黑点在白树上值为0,白点在黑树上值为0,进入点的值为正数,离开点的值为负数,那么对于一个翻转操作就是把黑白树对应的一段交换,打个标记就好,对于一次询问,分成上升和下降两段,上升取反跟下降的和加起来就是答案,注意最近公共祖先要特殊处理好。

【占坑待补】



发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表