A
直接模拟
#include <bits/stdc++.h> using namespace std; #define maxx 1000 #define INF 0x3f3f3f3f int n,m,a,b; int main() { int a,b,c,d,e,A,B,C,D; cin>>a>>b>>c>>d>>e>>A>>B>>C>>D; if(a*A+B*b+C*c-d*D>e) cout<<"YES\n"; else cout<<"NO\n"; }B
除了贪心,也可以采用动态规划
由于使用顺序不定,为了使得给定状态伤害达到最优,将两者从大到小排序,进行状态转移,状态定义为使用i次B,j次A,具体看代码
#include <bits/stdc++.h> using namespace std; #define maxx 1000 #define INF 0x3f3f3f3f int n,m,a,b,x,ans=INF; int A[maxx]; int B[maxx]; int dp[maxx][maxx]; bool cmp(int a,int b) { return a>b; } int main() { cin>>n>>m>>x; for(int i=1;i<=n;++i) cin>>A[i]; for(int i=1;i<=m;++i) cin>>B[i]; sort(A+1,A+1+n,cmp); sort(B+1,B+1+m,cmp); for(int i=0;i<=m;++i) for(int j=0;j<=n;++j) dp[i][j]=-INF; dp[0][0]=0; //B,A for(int i=1;i<=m;++i) for(int j=0;j<=n;++j) { dp[i][j]=dp[i-1][j]+B[i]; if(j>0) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+B[i]*A[j]); } ans=INF; for(int i=0;i<=m;++i) for(int j=0;j<=n;++j) if(dp[i][j]>=x) ans=min(ans,i+j); if(ans==INF) cout<<-1; else cout<<ans; }C
可以推导出公式,直接计算即可。
#include <bits/stdc++.h> using namespace std; #define maxx 1000 #define INF 0x3f3f3f3f #define int long long int n,m,a,b,c,x,t,ans=INF; int A[maxx]; int B[maxx]; int dp[maxx][maxx]; int pow(int a,int b) { int d=1; for(int i=1;i<=b;++i) d=d*a; return d; } signed main() { cin>>t; for(int i=1;i<=t;++i) {cin>>a>>b>>c; double ans=0; ans=(pow(a,16)+1820*(pow(a,12)*pow(b,4)+pow(a,12)*pow(c,4)))/pow(16,16); //cout<<ans<<"\n"; printf("%.12lf\n",ans); } }D
题目不严谨,应该说在这个题目内,两个字符串不同子序列选取位置不同,预处理出指定字母首尾长度指定的个数。
#include <bits/stdc++.h> using namespace std; #define maxx 1000 #define INF 0x3f3f3f3f #define int long long #define mod 998244353 int n,m,a,b,c,x,t,ans=INF; int A[maxx]; int B[maxx]; string s; int jc[maxx]; int jcf[maxx]; int dp[30][30][maxx]; //i字母开头,j字母结尾,长k的个数 int ksm(int a,int b,int n) { if(b==1) return a%n; int ans=ksm(a,b/2,n); ans=((ans%n)*(ans%n))%n; if(b%2==0) return ans; else return ((ans%n)*(a%n))%n; } int C(int i,int j) { return (jc[i]*((jcf[j]*jcf[i-j])%mod))%mod; } void build() { jc[0]=jcf[0]=1; for(int i=1;i<=600;++i) {jc[i]=(jc[i-1]*i)%mod; jcf[i]=(jcf[i-1]*ksm(i,mod-2,mod))%mod; } for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) for(int k=2;k<=j-i+1;++k) dp[s[i]-'a'][s[j]-'a'][k]=(dp[s[i]-'a'][s[j]-'a'][k]+C(j-i-1,k-2))%mod; } signed main() { cin>>n>>s>>m; s="0"+s; build(); for(int i=1;i<=m;++i) { char a,b; int x; cin>>a;getchar(); cin>>b;cin>>x; cout<<dp[a-'a'][b-'a'][x]<<"\n"; } }E.1
提供一种不用线段树的做法。
首先将两种方案从二维转化为一维数组,要求修改时实时修改给定的一维数组,并且维护map<int,int> M[2][char],即1维是哪一种方案,二维是所有小写字母,map<int,int>是指定方案与字母出现位置映射到指定方案字母位置是否出现,如果前三维度同时确定,那么不会重复,一个位置只有一个字母,维护此map的插入与删除,查询时,遍历所有小写字母,找到指定方案下每一个小写字母的大于当前位置的第一次出现位置,贪吃蛇可到达的位置一定小于这点,对除了当前位置的所有字母进行遍历,对位置取最小值,此最小值减1即为可到达的最大位置,因为此位置一定是一个字母的第一次出现位置,所以后面不可达,而此位置和行动位置之间不存在其他字母的第一次出现,即所有字母的第一次出现都在此外,故只有当前位置的字母,复杂度O(26nlogn)。
#include<bits/stdc++.h> using namespace std; #define maxx 444000 #define INF 0x3f3f3f3f #define int long long #define mod 1000000007 int x,y,t,a,b,n,m,p,c,k; char data_1[2][maxx]; map<int,int> M[2][maxx]; string s; int cal(int num,int x,int y) { if(num==0) { if(x%2==0) return m*(x-1)+m-y+1; else return m*(x-1)+y; } else { if(y%2==1) return n*(y-1)+x; else return n*(y-1)+n-x+1; } } void add(int num,char a,int which) { data_1[num][which]=a; ++M[num][a][which]; } signed main() { cin>>n>>m; getchar(); for(int i=1;i<=n;++i) {getline(cin,s); for(int j=1;j<=m;++j) { char a=s[2*(j-1)]; int e=cal(0,i,j); data_1[0][e]=a; ++M[0][a][e]; e=cal(1,i,j); data_1[1][e]=a; ++M[1][a][e]; } } int q; cin>>q; for(int i=1;i<=q;++i) { cin>>a>>b>>c; char d; if(a==1) {cin>>d; int e=cal(0,b,c); M[0][data_1[0][e]].erase(e); data_1[0][e]=d; ++M[0][d][e]; e=cal(1,b,c); M[1][data_1[1][e]].erase(e); data_1[1][e]=d; ++M[1][d][e]; } else if(a==2) { int e=cal(0,b,c); d=data_1[0][e]; int ans=n*m+1; for(int i='a';i<='z';++i) { if(i==d) continue; if(!M[0][i].empty()) { auto j=M[0][i].lower_bound(e); if(j!=M[0][i].end()) ans=min(ans,j->first); } } cout<<ans-e<<"\n"; } else if(a==3) { int e=cal(1,b,c); d=data_1[1][e]; int ans=n*m+1; for(int i='a';i<='z';++i) { if(i==d) continue; if(!M[1][i].empty()) { auto j=M[1][i].lower_bound(e); if(j!=M[1][i].end()) ans=min(ans,j->first); } } cout<<ans-e<<"\n"; } } }
E.2
提供一种使用线段树的方法,线段树维护两个值:1.线段树代表区间的首端点字母,2.从首端点的连续区间长。
支持单点的修改和区间的查询。
修改:修改点后用子节点更新父节点,更新方式为:父节点的1值为左子的值,如果左子连续区间为左子长,则连续区间为二者之和,否则为左子连续区间长。
查询:依照一般的线段树查询方式,不使用返回值,取代的,将另外将访问到的1值和目标值一致的区间的左端点,连续区间右端点加入向量,由于查询顺序可以保证区间呈现递增。查询完毕遍历向量,如果当前向量是第一个或者其左端点与前一个右端点差1,即这两个连续区间的合并连续,那么继续遍历,否则退出。
复杂度O(nlogn)
参考如下代码
#include<bits/stdc++.h> using namespace std; #define INF 1e18 #define maxx 8822000 #define maxx2 0x3f3f3f3f int a,b,d,k,m,t,n,ansmax,Smax,x,ans1,ans2,neww,c; int l,r; int xmax,ymax; int pj; int sum=0; char all[maxx][2]; vector<pair<int,int>> ans; string s; struct point { int sum; char a; }JD[maxx][2]; int ls(int root) { return 2*root; } int rs(int root) { return 2*root+1; } void update_from_son(int root,int l,int r,int which) { int mid=(l+r)/2; JD[root][which].a=JD[ls(root)][which].a; JD[root][which].sum=JD[ls(root)][which].sum; if(JD[ls(root)][which].a==JD[rs(root)][which].a&&JD[ls(root)][which].sum==mid-l+1) JD[root][which].sum=JD[root][which].sum+JD[rs(root)][which].sum; } void build(int root,int l,int r,int which) { if(l==r) { JD[root][which].sum=1; JD[root][which].a=all[l][which]; return; } int mid=(l+r)/2; build(ls(root),l,mid,which); build(rs(root),mid+1,r,which); update_from_son(root,l,r,which); } void modify(int root,int l,int r,int ql,int qr,char target,int which) { if(l==ql&&r==qr) { JD[root][which].a=target; return ; } int mid=(l+r)/2; if(ql<=mid) modify(ls(root),l,mid,ql,min(qr,mid),target,which); if(qr>=mid+1) modify(rs(root),mid+1,r,max(mid+1,ql),qr,target,which); update_from_son(root,l,r,which); } void query(int root,int l,int r,int ql,int qr,char target,int which) { if(l==ql&&r==qr) { if(target==JD[root][which].a) ans.push_back(pair<int,int>(l,l+JD[root][which].sum-1)); return ; } int mid=(l+r)/2; if(ql<=mid) query(ls(root),l,mid,ql,min(qr,mid),target,which); if(qr>=mid+1) query(rs(root),mid+1,r,max(mid+1,ql),qr,target,which); } int cal(int num,int x,int y) { if(num==0) { if(x%2==0) return m*(x-1)+m-y+1; else return m*(x-1)+y; } else { if(y%2==1) return n*(y-1)+x; else return n*(y-1)+n-x+1; } } signed main() { cin>>n>>m; getchar(); for(int i=1;i<=n;++i) {getline(cin,s); for(int j=1;j<=m;++j) { char a=s[2*(j-1)]; int e=cal(0,i,j); all[e][0]=a; e=cal(1,i,j); all[e][1]=a; } } build(1,1,n*m,0); build(1,1,n*m,1); int q; cin>>q; for(int i=1;i<=q;++i) { cin>>a>>b>>c; char d; if(a==1) {cin>>d; int e=cal(0,b,c); modify(1,1,n*m,e,e,d,0); all[e][0]=d; e=cal(1,b,c); modify(1,1,n*m,e,e,d,1); all[e][1]=d; } else if(a==2) { int e=cal(0,b,c); ans.clear(); query(1,1,n*m,e,n*m,all[e][0],0); int ans1=0; for(int i=0;i<ans.size();++i) {if(i==0||ans[i].first==ans[i-1].second+1) ans1=ans1+ans[i].second-ans[i].first+1; else break; } cout<<ans1<<"\n"; } else if(a==3) { int e=cal(1,b,c); ans.clear(); query(1,1,n*m,e,n*m,all[e][1],1); int ans1=0; for(int i=0;i<ans.size();++i) {if(i==0||ans[i].first==ans[i-1].second+1) ans1=ans1+ans[i].second-ans[i].first+1; else break; } cout<<ans1<<"\n"; } } }
F
给出一些证明
对于[l,r]的序列,计算出ai*(2^(r-i)),后取最大值,首先此表达是如果一个数字始终符合要求后被不断传递的表达,其次可以说明如果这个值是最大的,则这个数字在传递过程中不会被取代,因为若在i处取得最大值,在i以前的运算得到值都不如i,故到i后会被i取代,在i以后的运算值也不如i,故i也不会被取代。
对于[l,r]的序列,计算出ai*(2^(r-i)),后取最大值,首先此表达是如果一个数字始终符合要求后被不断传递的表达,其次可以说明如果这个值是最大的,则这个数字在传递过程中不会被取代,因为若在i处取得最大值,在i以前的运算得到值都不如i,故到i后会被i取代,在i以后的运算值也不如i,故i也不会被取代。
其次讨论如下:
1,非正数序列,如果有0,则有一项0,肯定是最大的且为0。
2.非正数序列,如果没有0,则在后30项有最大值,因为2^30>10^9,项数过多会超出范围比最后一项还小。
3.存在第一个正数,则向后找30项,因为到了这个项后,运算结果是此项的2倍,因为前面全是负数,而后30项运算后保证结果超出了10^9,后面的运算使得这个结果不会被取代。