B、干物妹小埋
theme:给定n栋楼的高度及到达该栋楼得到的开心值,开始可以从任一栋楼出发,下一步只会往前到>=它高度的楼,问能获得的最大开心值是多少?(0<n<=200000)
solution:就是求最长上升子序列,不过带了权,但数据范围较大,不能用常规DP,考虑用树状数组,每次就是求左边小于 该元素的以此元素结尾的子序列之和的最大值。现将楼高度从小到大排序后去重得到数组v,以排序后的数组的下标作为区间建树,则对于原数组a的每一个元素,找到>=它的第一个下标x,之后在[1,x+1]区间查询最大值,由于是按原顺序先查询后插入的,所以查询时在数组中且下标<=x的都<=a[i]
#include<bits/stdc++.h>
#define far(i,t,n) for(int i=t;i<n;++i)
#define endl "\n"
#define spa " "
#define pk(x) push_back(x)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ll tree[200020];
int lowbit(int x)
{
return x&(-x);
}
void update(int x,ll var)
{
while(x<200020)
{
tree[x]=max(tree[x],var);
x+=lowbit(x);
}
}
ll query(int x)
{
ll ans=0;
while(x)
{
ans=max(ans,tree[x]);
x-=lowbit(x);
}
return ans;
}
ll a[200020];
vector<ll>v;
int main()
{
int n;
cin>>n;
far(i,0,n)
{
scanf("%lld",&a[i]);
v.pk(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
ll ans=0;
far(i,0,n)
{
ll h;
scanf("%lld",&h);
int x=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;//树状数组下标从1开始
ll tmp=query(x);
ans=max(ans,tmp+h);
// cout<<x<<" "<<tmp<<" "<<ans<<endl;
update(x,tmp+h);
}
cout<<ans<<endl;
}
*G、说能过那是假的
theme:给定一个只由O、R、Z组成的字符串,问其中子序列ORZ个数
solution:求的子序列很短,o(n)时间即可求出。1' : 用数组r[i]记录每个i位置上为R的后续Z的个数,则从头遍历字符串,记录当前O的个数cnt,则每遇到一个R,ans+=cnt*r[i]
#include<stdio.h>
#include<string.h>
int main()
{
char ch[100007];
int len,ans=0;
scanf("%s",ch);
len=strlen(ch);
for(int i=0;i<len;i++)
for(int j=i+1;j<len;j++)
for(int k=j+1;k<len;k++)
if(ch[i]=='O'&&ch[j]=='R'&&ch[k]=='Z')
ans++;
printf("%d\n",ans);
}
2' : 数出所有Z的个数cntz,从头遍历一遍字符串,若为O,则cnto++,若为Z则cntz--,若为R,则ans+=cnto*cntz
#include<stdio.h>
#include<string.h>
int main()
{
char ch[100007];
int len;
scanf("%s",ch);
len=strlen(ch);
long long cnt1,cnt2;
cnt1=cnt2=0;
for (int i = 0; i < len;i++){
if(ch[i]=='Z') cnt2++;
}
long long ans=0;
for (int i = 0; i < len;i++){
if(ch[i]=='O') cnt1++;
if(ch[i]=='Z') cnt2--;
if(ch[i]=='R') ans+=cnt1*cnt2;
}
printf("%lld\n",ans);
}
hdu6536:Hello XTCPC
theme:给定一个字符串,求能形成序列xtCpc个数。1<=n<=2*10^5
solution:从前往。后记录每个相关字符已出现的个数,具体为如果所求序列中它前一个字符已出现的个数>=它才++,最后cnt[c]即为所求
#include <bits/stdc++.h>
using namespace std;
int n;
char s[200005];
int cnt[6];
int id(char x){
if(x == 'x') return 0;
if(x == 't') return 1;
if(x == 'C') return 2;
if(x == 'p') return 3;
if(x == 'c') return 4;
return -1;
}
int main()
{
while(~scanf("%d",&n)){
scanf("%s",s);
memset(cnt,0,sizeof cnt);
for(int i = 0;s[i];i++){
int tt = id(s[i]);
if(tt >= 0){
if(tt == 0) cnt[tt]++;
else if(cnt[tt-1] > cnt[tt]) cnt[tt]++;
}
}
printf("%d\n",cnt[4]);
}
return 0;
}
F、天花乱坠
theme:对于一个正n边形,每次取每条边的终点连接再构造一个n边形,无穷重复下去,问所有n边形的周长之和为多少?最外边边长都为100.
solution: 设最外面边长为1,则下一个n边形边长为,所以其实就是以1为首项,为公比的等比数列极限求和为
#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
double pi=acos(-1);
int main()
{
int n;
int len=100;
while(cin>>n)
{
double angle=pi/n;
double ans=n*len;
ans=ans/(1-cos(angle));
printf("%.2f\n",ans);
}
}