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