7.8 月赛补题+cf 569div2

B(暴力

Q:统计两个正整数t1 、 t2 之间的所有数的约数个数和S,给出的两个数t1、t2为10000000以内的正整数。

A:暴力,在1到t2之间枚举因子r ,t2/r-t1/r 即t1~t2之间包含该因子的数的个数 ,求和即t1 、 t2 之间的所有数的约数个数和

 

C(贪心,中位数

Q:http://47.96.116.66/problem.php?cid=1385&pid=2

A:关于改变中位数为目标值的贪心,修改次数最小的最优方案是 目标值大于当前中位数时,改变任意小于目标值的数为目标值;

目标值小于当前中位数时,改变任意大于目标值的数为目标值; 直到中位数改变

 

D(欧拉函数

从原点选择任意方向射线,对于给定c,问在(x,y)(|x| <= N , |y| <= N)范围内有多少方向恰好有c个整点;

1.由于对称性,只考虑 区域d:x〉=0 ,y〈=x 即可;

2.对于每一区域,任意一点(x,y)与原点连线所经整点数目为:min(n/(x/gcd),n/(y/gcd))在区域d内有 y〈=x,min(n/(x/gcd),n/(y/gcd))可化简为 n/(y/gcd)

3.由于(y/gcd)与(x/gcd)互质,对于满足n/(x/gcd)==c的x/gcd,所有比其小的与其互质的数可作为(y/gcd)的取值。可满足的方案数即x/gcd的欧拉函数phi(x/gcd);

欧拉函数用欧拉筛递推求取。

 

/*
特性 :
1.若a为质数,phi[a]=a-1;
2.若a为质数,b mod a=0,phi[a*b]=phi[b]*a
3.若a,b互质,phi[a*b]=phi[a]*phi[b](当a为质数时,if b mod a!=0 ,phi[a*b]=phi[a]*phi[b])
*/
int m[n],phi[n],p[n],nump;
//m[i]标记i是否为素数,0为素数,1不为素数;p是存放素数的数组;nump是当前素数个数;phi[i]为欧拉函数
int main()
{
        phi[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!m[i])//i为素数
        {
            p[++nump]=i;//将i加入素数数组p中
            phi[i]=i-1;//因为i是素数,由特性得知    
        }    
        for (int j=1;j<=nump&&p[j]*i<=n;j++)  //用当前已得到的素数数组p筛,筛去p[j]*i
        {
            m[p[j]*i]=1;//可以确定i*p[j]不是素数 
            if (i%p[j]==0) //看p[j]是否是i的约数,因为素数p[j],等于判断i和p[j]是否互质 
            {
                phi[p[j]*i]=phi[i]*p[j]; //特性2
                break;
            }
            else phi[p[j]*i]=phi[i]*(p[j]-1); //互质,特性3其,p[j]-1就是phi[p[j]]   
        }
    }
}

 

 

ac代码:

include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#define inf 0x3f3f3f3f
#define N 5000005
#define mod 10000000007
#define mem(a,x) memset(a,x,sizeof(a))
#define ll long long
#define exp 1e-8
#define lowbit(x) (x&-x)
#define rep(i,x,y) for(int i=x;i<=y;i++)
using namespace std; 
int tot=0 ,prime[N] ,isprime[N] ,phi[N];
void geteuler( int maxn ){
     phi[1] = 1;
     mem( isprime ,1 );
     isprime[1] = 0;
     for( int i=2 ;i<=maxn ;i++){
        if( isprime[i]){
            prime[tot++] = i;
            phi[i] = i-1;
        }
        for( int j=0 ;j<tot&&prime[j]*i<=maxn ; j++){
            isprime[i*prime[j]] = 0;
            if( i%prime[j]==0 ){
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
             }
            phi[i*prime[j]] = phi[i]*phi[prime[j]];
        }
     }     
}
 
int main()
{
    int m ,c;
    scanf("%d%d" ,&m ,&c);
    geteuler( N );
    ll ans = 0;
    for( int i=1 ;i<=m ;i++ ){
        if( m/i==c )ans += phi[i];
        if( m/i<c )break;
    }
    printf("%lld",ans*8);
    return 0;
}
  

 

F

A: 即全错排公式 :

2阶递推形式 :D(1) = 0, D(2) = 1. D(n) = (n-1) [D(n-2) + D(n-1)]

1阶递推形式:D(1) = 0 , D(n) = nD(n-1)-(-1)^n

 

cf569 div2:

D (归纳推理,构造

Q:给定一种方案 ,在每一次的位移(dx,dy)不重复使用的情况下,不重复的遍历n*m的区域

A:当n==1时 ,易构造一种方案为:(1,1)->(1,n)->(1,2)->(1,n-1)->(1,3)......

      当n>1时,由之前的构造归纳出此时应该构造中心对称的走法

 

     rep( i ,1 ,(n+1)/2){
         if( i!=n+1-i )
            rep( j ,1 ,m){
             printf("%d %d\n" ,i ,j);
             printf("%d %d\n" ,n+1-i ,m+1-j);
            }
         else
            rep( j ,1 ,(m+1)/2){
             printf("%d %d\n" ,i ,j);
             if( j == m+1-j)break;
             printf("%d %d\n" ,n+1-i ,m+1-j);
            }
     }

 

 E(线段树

Q: 给你n个物品(每个物品一个)的价格和m个现在依次排在队里的人手上的钱,每个人都会尽可能把钱用完去买一样东西,即买当前物品中能买的价值最高的,不能买就不买走人,问m个人排完之后剩下的价值最高的物品价格是多少。

A:题意即找出一个最大的商品价格x ,使得价格>=x的商品数量大于财产>=x的人数;

以价格为操作区间,类似于前缀和,1到物品的价格区间+1操作,1到人手里钱的区间-1操作,查询只要有出现正的值,这个价格就可以成为一个备选的目标,由于需要找的是一个最大的值,所以在查询时要优先查右区间,再查左区间。

#include<bits/stdc++.h>
#define rep( i ,x ,y) for(int i=x ;i<=y ;i++)
#define lson pos<<1
#define rson pos<<1|1
using namespace std;

typedef long long ll;
const int maxn= 1e6+5;
int tree[maxn<<2] ,lazy[maxn<<2] ,n ,m;
int a[maxn] ,b[maxn];

inline void push_down( int pos){
    if( lazy[pos]){
        lazy[lson] += lazy[pos];
        lazy[rson] += lazy[pos];
        tree[lson] += lazy[pos];
        tree[rson] += lazy[pos];
        lazy[pos] = 0;
    }
}

inline void push_up( int pos ){
    tree[pos] = max( tree[lson] ,tree[rson]);
//    cout<<pos<<" "<<tree[pos]<<endl;
}

void updata( int L ,int R ,int l ,int r, int pos ,int v ){
//    cout<<L <<" "<<R <<" "<<pos<<endl;
     if( L>=l && R<=r){
         lazy[pos] += v;
         tree[pos] += v;
         return;
     }
     push_down( pos );
     int mid = (L+R)>>1;
     if( l <= mid ) updata( L ,mid ,l ,r ,lson ,v);
     if( r > mid ) updata( mid+1 ,R ,l ,r ,rson ,v );
     push_up(pos); 
     //cout<<L <<" "<<R<<" "<<l<<" "<<r<<" "<<v<<endl;
}

int query( int L ,int R ,int pos){
    if( L==R )return R;
    push_down( pos );
    int mid = (L+R)>>1;
    if( tree[rson]>0 ) return query( mid+1 ,R ,rson );
    //查询时优先查询右子树 
    return query( L ,mid ,lson );
}

int main( ){
    scanf("%d%d" ,&n ,&m);
    rep( i ,1 ,n)scanf("%d" ,&a[i]) ,updata(1 ,maxn ,1 ,a[i], 1 ,1);
    //商品价格区间标记+1 
    rep( i ,1 ,m)scanf("%d" ,&b[i]) ,updata(1 ,maxn ,1 ,b[i], 1 ,-1);
    //人手财产区间标记-1 
    
    int q;
    scanf("%d" ,&q );
    int op ,id ,v;
    while( q-- ){
        scanf("%d%d%d" ,&op ,&id ,&v );
        if( op==1 ){
            updata( 1 ,maxn ,1 ,a[id] ,1 ,-1 );
            //先取消原区间 
            updata(1 ,maxn ,1 ,v ,1 ,1 );
            //再更新新区间 
            a[id] = v;
        }
        else{
            updata(1 ,maxn ,1 ,b[id] ,1 ,1);
            updata(1 ,maxn ,1 ,v ,1 ,-1);
            b[id] = v;
        }
        if( tree[1] >0 )printf("%d\n" ,query(1 ,maxn ,1));
        else printf("-1\n");
    }
    return 0;
}
posted @ 2019-07-09 12:12  易如鱼  阅读(222)  评论(0编辑  收藏  举报