T1
金币
很简单的题,控制天数这个变量
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=100010; const int INF=0x3fffffff; int x; int main(){ cin>>x; int ans=0,b=0; for(int i=1;i<=x;i++){ for(int j=1;j<=i;j++){ ans+=i; b++; if(b>=x) break; } if(b>=x) break; } cout<<ans<<endl; return 0; }
T3 求和
这道题还是比较好的
涉及公式推理,推理很重要!!!!
https://blog.csdn.net/ThinFatty/article/details/53054377?locationNum=4&fps=1
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=1010; const int INF=0x3fffffff; //欸这种题我就只想得到暴力。。。。 //但是很明显不行的。。。仔细思考应该需要找规律,推公式。。。 简化 //很好的一道题 long long num[100001],col[10001]; struct node{ long long ab,a,b,op; //op是出现次数,ab是预处理的sum(编号*数字),a是预处理的sum数字,b是预处理的sum标号 }d[10001][2]; int n,m; int main(){ scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&num[i]); for(int i=1;i<=n;i++) { scanf("%lld",&col[i]); d[col[i]][i%2].op++; d[col[i]][i%2].ab+=i*num[i]; d[col[i]][i%2].a+=num[i]; d[col[i]][i%2].b+=i; d[col[i]][i%2].ab%=10007; d[col[i]][i%2].a%=10007; d[col[i]][i%2].b%=10007; }; long long ans=0; for(int i=1;i<=m;i++){ for(int j=0;j<=1;j++){ if(d[i][j].op<2) continue; ans+=(d[i][j].op-2)*d[i][j].ab; ans%=10007; ans+=d[i][j].a*d[i][j].b; ans%=10007; } } printf("%lld ",ans); return 0; }
T4 推销员
先来分析一下题目。从题目中的样例,我们可以得到一个猜想:后面的决策一定包含前面的决策。这个结论是可以证明的,证明过程这里就不赘述了。因此,我们只需要分阶段一步步在决策中添加住户即可。对于某一个决策,我们设离入口最远的住户编号是x,编号为i的住户离入口的距离是s[i],添加的疲劳值是a[i],则要添加住户无非就是两种情况:一是在最远住户之前找一个住户添加入决策中,这样新累积的疲劳值是a[i];二是在最远住户之后找一个住户添加入决策中,这样新累积的疲劳值是a[i]+s[i]*2-s[x]*2。对于这两种情况分别找出添加的疲劳值的最大值,然后再进行选择即可。可是,用直接的方法找最大值是O(n)的,这样使得整个程序是O(n^2),又因为n可达100000,因此这个方案不可行。此时我们就可以采用优先队列来处理,将找最大值的复杂度减小到O(logn)。用两个优先队列Q1,Q2分别表示最远住户前面的住户所添加的疲劳值组成的队列和最远用户后面的住户所添加的疲劳值组成的队列,其中要注意的是,Q1中第i住户所对应的元素是a[i],而在Q2中第i住户所对应的元素是a[i]+s[i]*2。然后,对于每次决策,分别取Q1和Q2的顶端元素,比较Q1的顶元素和Q2的顶元素-s[x]*2(相当于比较a[i]和a[j]+s[j]*2-s[x]*2,即两个住户所新添加的疲劳值),如果选择最远住户前面的住户,则将答案累加后直接pop即可,如果选择最远住户后面的住户,就要注意将x后移,并将新的已成为最远住户前面住户的元素加入Q1中。在操作过程中,我们用一个v数组标记该住户是否被选过,以便在提取最大值的时候不出现重复。
根据题目的数据范围,也能知道不能暴力或者dp
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=100010; const int INF=0x3fffffff; //这道题的数据规模明显说明了不能用暴力或者dp //于是思考,怎样对下一步进行选择,因为这个是一步一步的选择下去的,后面的包含前面的,每次都在还没有走过的需要消耗最多能力的那个选择 //那么怎么选择,可以分为从当前位置向前走或者是向后走,那么就有两种选择, //向前走,不用走多余的路,那么疲惫值就是a[i] //向后走,需要走多余的路,那么疲惫值就是a[i]+2*s[i] (当然需要剪掉) //那么为了提高效率,设置两个优先队列,分别存储前面的和后面的疲惫之最大的 typedef pair<int,int> pa; priority_queue<pa> q1,q2; //priority_queue<pa> temp1,temp2; //用一个v数组标记该住户是否被选过,以便在提取最大值的时候不出现重复 int n; bool vis[maxn]={0}; int dis[maxn],tri[maxn]; int main(){ cin>>n; for(int i=1;i<=n;i++) scanf("%d",&dis[i]); for(int i=1;i<=n;i++) { scanf("%d",&tri[i]); q2.push(make_pair(tri[i]+2*dis[i],i)); //q2在这里放进去 } int ans=0; int index=0; //这个是现在到的那个地方 for(int i=1;i<=n;i++){ while(!q1.empty()&&vis[q1.top().second]) q1.pop(); //把访问过的都弹出去 while(!q2.empty()&&(vis[q2.top().second]||q2.top().second<index)) q2.pop();//访问过的并且已经小于当前的了就弹出去 int t1=-1,t2=-1; if(!q1.empty()) t1=q1.top().first; if(!q2.empty()) t2=q2.top().first-2*dis[index]; //两个疲惫值 if(t1>t2){ //选前面的,那么就要q1弹出去 vis[q1.top().second]=1; ans+=t1; q1.pop(); } else{ vis[q2.top().second]=1; ans+=t2; while(index<q2.top().second){ //q1在这里放进去,现在位置之前的 q1.push(make_pair(tri[index],index)); index++; } q2.pop(); } printf("%d ",ans); /* temp1=q1; while(!temp1.empty()){ cout<<temp1.top().second<<" "; temp1.pop(); } cout<<endl; temp2=q2; while(!temp2.empty()){ cout<<temp2.top().second<<" "; temp2.pop(); } cout<<endl; */ } return 0; }