本场总结:

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;
    }
}