本场总结:
A.模拟暴力
B.猜结论
C.贪心
D.签到
E.离散化+树状数组+二分 ----插入区间元素找中位数
H.数位dp
J.签到
小结:
----多项式可约问题,三次多项式实数范围内可约.二次多项式根判别式.
----注意数据范围再贪心,一般比较小的值作为索引进行贪心.
----插入区间元素可以用两个树状数组维护,查找中位数一般就是二分
----练习数位dp
A. String
大致题意:给定一个01串,请把它划分为尽量少的子串,每个子串是该子串循环移位的字典序最小的串.
分析:暴力找就行,check判断.
#include<bits/stdc++.h>
using namespace std;
bool check( string s )
{
int n=s.size();
for( int i=1;i<n;i++ )
{
for( int j=0;j<n;j++ ) // 自循环就是依次让每个数字都做一次最高位
{
if( s[j]<s[(i+j)%n] ) break;
if( s[j]>s[(i+j)%n] ) return false;
}
}
return true;
}
int main()
{
int t;
cin>>t;
while( t-- )
{
string a;
cin>>a;
int i=0;
int n=a.size();
while( i<n )
{
for( int j=n-i;j>0;j-- )
{
if( check( a.substr(i,j) ) )
{
cout<<a.substr(i,j);
i+=j;
if( i<n ) cout<<" ";
break;
}
}
}
cout<<endl;
}
return 0;
}B. Irreducible Polynomial
大致题意:给定一个多项式,判断在实数范围内是否可约.提取常数项不算可约.
分析:有一个结论,对于实数范围内n>=3次方的多项式必然可约,而n==2只需判断根判别式.
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while( t-- )
{
int n;
cin>>n;
int a,b,c;
if( n<=1 )
{
for( int i=0;i<=n;i++ )cin>>a;
puts("Yes");
}
else if( n==2 )
{
cin>>a>>b>>c;
if( 1ll*b*b<4ll*a*c ) puts("Yes");
else puts("No");
}
else
{
for( int i=0;i<=n;i++ )cin>>a;
puts("No");
}
}
}C. Governing sand
大致题意:有很多树,每棵树不同种类砍掉花费也不同,高度也不同,现要砍掉一些树让最高的树的数量是所有树的数量之和的一半以上,请计算最小花费.
分析:每颗数的花费是<=200的,那么我们可以先按树的高度进行排序,然后使当前树为最高的高度去计算花费,对于之前的树开一个200空间的数组按花费维护即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n;
ll sum,maxx,g[205];
struct node{
ll h,p,c;
}t[maxn];
bool cmp( node x,node y )
{
return x.h<y.h;
}
int main()
{
while( ~scanf("%d",&n) )
{
sum=0;
for( int i=1;i<=n;i++ )
{
scanf("%lld%lld%lld",&t[i].h,&t[i].c,&t[i].p);
sum+=t[i].p*t[i].c; // 所有树全砍掉 的花费
}
sort(t+1,t+1+n,cmp);
maxx=t[1].c*t[1].p;
for( int i=1;i<=200;i++ ) g[i]=0;
int tot=0;
ll fsum=0,max1=0;
for( int i=1;i<=n;i++ )
{ // 选定当前高度的树为最高
fsum+=t[i].p; // ----- 树的数量
max1+=t[i].c*t[i].p; // 树的价值
if( i==n || t[i].h!=t[i+1].h )
{
fsum--;
for( int j=200;j>=1;j-- )
{
if( fsum<=g[j] )
{
max1+=j*fsum;
break;
}
else
{
max1+=j*g[j];
fsum-=g[j];
}
}
maxx=max(max1,maxx);
fsum=0,max1=0;
while( tot<i )
{
tot++;
g[t[tot].c]+=t[tot].p;
}
}
}
printf("%lld\n",sum-maxx);
}
} D. Number
大致题意:给定一个质数p,请找一个n位数能够整除p.
分析:签到题.n<p的位数 则为没有,n>=p的位数,输出p,后面位数补0即可.
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
string p;
cin>>n>>p;
if( n==p.size() ) cout<<p;
else if( n>p.size() )
{
cout<<p;
for( int i=0;i<n-p.size();i++ ) cout<<"0";
}
else cout<<"T_T";
}E.Find the median
大致题意:开始一个空序列,n次操作,每次插入[li,ri]区间的数.然后输出序列的中位数的值.li,ri的给定计算公式.
分析:我们要记录序列个数和序列元素,题意是按区间插入元素,那么我们可以利用差分的思想,在l位置插入-l,在r的位置插入r+1,用一个树状数组去维护,那么前缀和即使当前序列的元素个数.那么中位数怎么找呢。要运用二分的思想去check中位数,对于当前中位数假如它在一段l-r区间数里面,如何计算[l,pos]个数呢。我们可以再用一个树状数组来标记.插入[li,ri]区间的数,在li位置标记+1,ri位置标记-1,计算前缀和,如果前缀和大于0的话那么肯定在某一段或者多段区间里面,那么少了右端点,但对于当前check右端点就是mid,所以加上缺失右端点的左端点个数*(mid+1)就能计算个数了.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e5+10;
int x[maxn],y[maxn],l[maxn],r[maxn];
int num[maxn<<1],cnt,n;
ll sum1[maxn<<2],sum2[maxn<<2];
int lowbit(int x ) { return x&(-x); }
void add( ll a[],int x,int C ) { while( x<=cnt ) a[x]+=C,x+=lowbit(x); }
ll query( ll a[],int x ) { ll ans=0; while( x ) ans+=a[x],x-=lowbit(x); return ans; }
void init()
{
ll a1,b1,a2,b2,m1,m2,c1,c2;
scanf("%d",&n);
scanf("%d%d%lld%lld%lld%lld",&x[1],&x[2],&a1,&b1,&c1,&m1);
scanf("%d%d%lld%lld%lld%lld",&y[1],&y[2],&a2,&b2,&c2,&m2);
for( int i=1;i<=n;i++ )
{
if( i>2 ) x[i]=( a1*x[i-1]+b1*x[i-2]+c1 )%m1;
if( i>2 ) y[i]=( a2*y[i-1]+b2*y[i-2]+c2 )%m2;
l[i]=min( x[i],y[i] )+1;
r[i]=max( x[i],y[i] )+1;
num[++cnt]=l[i];
num[++cnt]=r[i]+1;
}
sort(num+1,num+1+cnt);
cnt=unique(num+1,num+cnt+1)-num-1;
}
int get_id( int x ) { return lower_bound(num+1,num+cnt+1,x)-num; }
void solve()
{
ll all=0;
for( int i=1;i<=n;i++ )
{
all+=r[i]-l[i]+1;
int L=get_id(l[i]);
int R=get_id(r[i]+1);
add(sum1,L,-l[i]);add(sum1,R,r[i]+1);
add(sum2,L,1); add(sum2,R,-1);
L=1,R=1e9;
while( L<R )
{
int mid=(L+R)/2;
int pos=upper_bound(num+1,num+1+cnt,mid)-num-1;
ll tmp=query(sum1,pos)+query(sum2,pos)*(mid+1);
if( tmp<(all+1)/2 ) L=mid+1;
else R=mid;
}
printf("%d\n",L);
}
}
int main()
{
init();
solve();
} H.Pair
大致题意:给定三个数字a,b,c,找无序对(x,y)满足 x^y<c 或者 (x&y)<c 的个数. ( 1<=x<=a, 1<=y<=b )
分析:显然是一道数位dp的题.坑点就是要减去 x==0 和 y==0的情况.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int bita[50],bitb[50],bitc[50];
ll dp[50][2][2][2][2];
ll dfs( int len,int ma,int mb,int ad,int xo )
{
if( !len ) return 1;
if( dp[len][ma][mb][ad][xo]>=0 ) return dp[len][ma][mb][ad][xo];
ll ans=0;
int upa=ma ? bita[len] : 1;
int upb=mb ? bitb[len] : 1;
for( int i=0;i<=upa;i++ )
{
for( int j=0;j<=upb;j++ )
{
if( ad && (i & j)>bitc[len] || xo && ( i^j )<bitc[len] ) continue;
ans+=dfs(len-1,ma && ( i==upa ), mb && ( j==upb ),
ad && ( (i & j)==bitc[len] ),xo && ( ( i^j )==bitc[len] ) );
}
}
dp[len][ma][mb][ad][xo]=ans;
return ans;
}
ll solve( ll a,ll b,ll c )
{
int num1=0,num2=0,num3=0;
while( a ) { bita[++num1]= a&1 ;a>>=1; }
while( b ) { bitb[++num2]= b&1 ;b>>=1; }
while( c ) { bitc[++num3]= c&1 ;c>>=1; }
int len=max( num1,max(num2,num3) );
return dfs(len,1,1,1,1);
}
int main()
{
int t;
scanf("%d",&t);
while( t-- )
{
memset(dp,-1,sizeof(dp));
memset(bita,0,sizeof(bita));
memset(bitb,0,sizeof(bitb));
memset(bitc,0,sizeof(bitc));
ll a,b,c;
scanf("%lld%lld%lld",&a,&b,&c);
ll ans=solve(a,b,c);
ans-=max(0ll,a-c+1);
ans-=max(0ll,b-c+1);
printf("%lld\n",a*b-ans);
}
}J. A+B problem
大致题意:给定a,b两个数。求a的逆序数加上b的逆序数的和的逆序数.
分许:签到题,模拟即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rev( ll x )
{
ll ans=0,cnt=0;
while( x )
{
ans*=10;
ans+=x%10;
x/=10;
}
return ans;
}
int main()
{
int t;
cin>>t;
while( t-- )
{
ll a,b;
cin>>a>>b;
cout<<rev(rev(a)+rev(b))<<endl;
}
}
京公网安备 11010502036488号