这场的前三题都挺水的,第4题骗分也有80,挺良心的。。
A 小L的作文
算送分题,直接一个for循环计数一下就可以了。
B 小L的多项式
根据题意,f(x)可以O(n)计算得到,求f(x1)...f(xm)的话,再for循环一遍就可以了。复杂度O(n*m)。
由于n,m<=1000,所以可以直接计算。C 小L的编辑器
对于本题,我是这么考虑的。
先输入第一个字母建立一个根节点,然后如果往左就在左子树上建一个新节点,如果往右就在右子树上新建点,然后转移到当前点继续相同的操作。
那么会发现最后的结果是这棵树的中序遍历输出。
代码:
#include<bits/stdc++.h> using namespace std; string s,t; int tot=0,vis[100]; struct node{ char now; int l,r; }p[1000040]; void dfs(int x){ if(x==s.size())return; if(t[x]=='L'){ p[tot].l=tot+1; tot++; p[tot].now=s[x+1]; } else { p[tot].r=tot+1; tot++; p[tot].now=s[x+1]; } dfs(x+1); } void dfs2(int x){ if(x==0)return ; dfs2(p[x].l); cout<<p[x].now; dfs2(p[x].r); } int main() { cin>>s>>t; tot=1; p[tot].now=s[0]; dfs(0); tot--; p[tot].l=p[tot].r=0; dfs2(1); return 0; }
D 小L的数列
根据题目的条件要求,显然可以先把a数组进行一遍排序。
很容易想到用dp[i]表示前i个数能排列出来的最大的好序列长度。
类似LIS的做法,我们强制第i个必选。
那么有dp[i]=max(dp[i],dp[j]+1) j<i&&gcd(a[i],a[j])>1
这样做的复杂度是O(n^2),居然有80分!
想不到优化了,赛后看了题解。
参考了一个思路是,枚举每个数的因子,然后用dp[i]表示因子i为结尾的时候所能构成的最大长度。
即对于一个数a[i],我们枚举他的因子j(sqrt(a[i])复杂度),找到最大的dp[j]赋值给maxi。接着再枚举一遍a[i]的因子j,将所有的dp[j]=maxi+1。注意特判a[i]本身为质数的情况!
上面的做法其实本质和原暴力dp是相似的,在暴力dp的时候我们是往前找数,并且找的数要和a[i]有相同的因子。那么用dp[i]表示i为因子的话就不用再找数了,只要找一个最大长度的因子就可以了。然后为了维护dp[i],我们需要把a[i]里所有的因子都变成这个新的最大长度。
太妙了!
代码:
#include<bits/stdc++.h> using namespace std; int n,a[100040],dp[100004]; int main() { int T; cin>>T; while(T--){ cin>>n; int maxi=1; for(int i=1;i<=n;i++)cin>>a[i]; memset(dp,0,sizeof(dp)); sort(a+1,a+1+n); for(int i=1;i<=n;i++){ int tmp=0; int t=a[i]; for(int j=2;j<=sqrt(a[i]);j++){ if(a[i]%j==0){ tmp=max(tmp,dp[j]); while(t%j==0)t/=j; } } if(t>1)tmp=max(tmp,dp[t]); //特判a[i]为质数的情况 for(int j=2;j<=sqrt(a[i]);j++){ if(a[i]%j==0)dp[j]=tmp+1; } if(t>1)dp[t]=tmp+1; } for(int i=1;i<=a[n];i++)maxi=max(maxi,dp[i]); cout<<maxi<<endl; } return 0; }