• 牛客挑战赛83


    牛客练习赛83

    A追求女神

    注意到小L可以准时到达当且仅当,时间与两个位置的莫比雪夫距离之差是2的倍数。
    然后直接判断即可。

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int read(){
    	int sum=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9'){
    		sum=sum*10+ch-'0';
    		ch=getchar();
    	}
    	return sum*f;
    }
    int main(){
    	int T=read();
    	while(T--){
    		int n=read();
    		int ans=1;
    		int t=0,x=0,y=0;
    		int x0,y0,t0;
    		for(int i=1;i<=n;i++){
    			x0=x,y0=y,t0=t;
    			t=read(),x=read(),y=read();
    			if(abs(x-x0)+abs(y-y0)>abs(t-t0)){
    				ans=0;
                    continue;
    			}
    			else if((abs(t-t0)-abs(x-x0)-abs(y-y0))%2!=0){
    				ans=0; 
    				continue;
    			}
    		}
    		if(ans==1)printf("Yes
    ");
    		else printf("No
    ");
    	}
    	return 0;
    } 
    

    B 计算几何

    (T<=10 l,r<=10^{18})

    数位DP可以解决。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    #define int long long
    int read(){
    	int sum=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    int m[100];
    void init(){
    	m[0]=1;
    	for(int i=1;i<=61;i++)m[i]=m[i-1]*2;
    }
    int get(int x){
    	int ans=0,cnt=0;
    	for(int i=61;i>=1;i--){
    		if(x>=m[i])ans+=m[i-1],x-=m[i],cnt++;
    	}
    	if(cnt%2==1||x==1)ans++;
    	return ans;
    }
    signed main(){
    	init();
    	int T=read();
    	while(T--){
    		int l=read(),r=read();
    		printf("%lld
    ",get(r)-get(l-1));
    	}
    	return 0;
    }
    

    但是通过求ans的过程发现其实整个过程可以化简

    C 集合操作


    显然可以把集合中的数用(a+b*p (0<=a<p))表示。
    这样表示有两个好处:
    1、(b)大的数比(b)小的数要大。
    2、每一次操作就是使集合中的数的(b)减一。

    可以将题目简化成割韭菜。
    二分求出最多割韭菜的高度(粗割韭菜)。
    如果这个时候(k)还有剩余就在最高的哪些韭菜中找(a)最大的割。
    复杂度(O(log(long long)*n))

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define N 1010000
    #define int long long
    int c[N];
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    struct Num{
    	int a;
    	int b;
    }num[N];
    bool operator <(Num x,Num y){
    	return x.a>y.a;
    }
    signed main(){
    	int n=read(),k=read(),p=read();
    	if(p==0||k==0){
    		for(int i=1;i<=n;i++)c[i]=read();
    		sort(c+1,c+1+n);
    		for(int i=1;i<=n;i++)printf("%lld ",c[i]);
    		return 0;
    	}
    	for(int i=1;i<=n;i++){
    		int x=read();
    		num[i].a=x%p;
    		num[i].b=x/p; 
    	} 
    	int ans=0;
    	int L=-(1e18),R=(1e18);
    	while(L<=R){
    		int mid=(L+R)/2;
    		int cnt=0; 
    		for(int i=1;i<=n;i++){
    			cnt+=((num[i].b-mid)>0?(num[i].b-mid):0);
    			if(cnt>k)break;
    		}
    		if(cnt<=k)R=mid-1;
    		else {
    			ans=mid;
    			L=mid+1;
    		}
    	}
    	ans++;
    	int cnt=0;
    	for(int i=1;i<=n;i++){
    		cnt+=max(num[i].b-ans,(int)0);
    		num[i].b=min(num[i].b,ans);
    	}
    	k-=cnt;
    	sort(num+1,num+1+n);
    	for(int i=1;i<=n;i++){
    		if(k==0)break;
    		if(num[i].b==ans)num[i].b-=1,k--;
    	}
    	for(int i=1;i<=n;i++)c[i]=num[i].b*p+num[i].a; 
    	sort(c+1,c+1+n);
    	for(int i=1;i<=n;i++)printf("%lld ",c[i]);
    	return 0;
    } 
    

    D 数列递推


    考虑(i mod j)实际上等价于(i-j*lfloorfrac{i}{j} floor)
    (lfloorfrac{i}{j} floor)可以联想到整除分块。
    可以记录一个前缀和数组(sum[i][j])代表以第(i)个数为结尾两个数之间间隔(j)(f)的前缀和。
    只记录(lfloorfrac{i}{j} floor<=sqrt(n))时的前缀和。
    对于每一个(i)
    对于(lfloorfrac{i}{j} floor<=sqrt(i))的部分用前缀和求解。
    对于(lfloorfrac{i}{j} floor>sqrt(n))的部分可以证明满足这种条件的j不超过sqrt(i)个,可直接累加。
    复杂度(O(nsqrt[2]{n}))

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define int long long
    #define p 998244353
    #define N 101000
    int sum[N][333];
    int f[N];
    int read(){
    	int sum=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    } 
    signed main(){
    	int n=read();
    	f[0]=read();
    	for(int i=1;i<=n;i++)sum[0][i]=f[0];
    	for(int i=1;i<=n;i++){
    		f[i]+=f[0];
    		for(int l=2,r;l<=i;l=r+1){
    			r=i/(i/l);
    			if(i/l>sqrt(n)){
    				for(int j=l;j<=r;j++)
    					f[i]=(f[i]+f[i%j])%p;
    				continue;
    			}
    			f[i]=(f[i]+sum[i-l*(i/l)][i/l]-sum[i-r*(i/l)][i/l]+f[i-i/l*r]+p)%p;
    		}
    		for(int j=1;j<=sqrt(n);j++)
    			if(i>=j)sum[i][j]=(sum[i-j][j]+f[i])%p;
    			else sum[i][j]=f[i];
    	}
    	for(int i=1;i<=n;i++)printf("%lld ",f[i]);
    	return 0;
    }
    

    E 小L的疑惑



    当年NOIP的题,现在居然不会了。
    不能表示的第一大是(a*b-a-b)
    可以用同余类得出。设同余类的大小为(b-1)(a)(b)互质,每次会填上一个同余类的空,不算(0),填满要(b-1)次。
    对于每个空(即(mod b)结果相同的一组数)单独考虑,第一次填上表示的数显然是这组数中可以用(a)(b)表示的最小的。然后再减一次模数(b)就是最大不能表示的数。
    然后找到每组数中最大的。
    显然就是同余类最后被填上的空所代表的的那组数即((b-1)*a-b)

    还有一个比较好的数学解法
    小凯的疑惑数学解法

    NOIP这题解法很多,但是大多都看不懂

    然后考虑怎么求第k大。

    同样考虑同余类。同样考虑每一个空分开。
    只需要找到每一组最大的那一个不能表示的数(x_i)(x_i-b)同样不能表示,这样就能找到全部的不能表示的数。
    然后想怎么找到每一组最大的那一个不能表示的数。
    根据求第一大时的结论

    对于每个空(即(mod b)结果相同的一组数)单独考虑,第一次填上表示的数显然是这组数中可以用(a)(b)表示的最小的。然后再减一次模数(b)就是最大不能表示的数。

    只需找到第一次填上的数也就是((b-x)*a)
    综上可以发现,一个数不能表示当且仅当这个数可以表示为(ab-a-b-x_1a-x_2b(x_1,x_2 inmathbb{N})),也就是最大不能表示的数减去若干个(a)(b)

    具体的话,

    假设 a<b 由于答案不能算重则当减去(b)时就不能在减去(a)

    维护两个队列,一个队列表示当前能减去(a),另外仅能减去(b),那么每次在两个队头取最大的比较即可。

    正确性类比(NOIP)蚯蚓(单调性)。时间复杂度$ mathcal O(k)$。

    我用一个优先队列一个队列水过了。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define int long long
    int read(){
        int sum=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            sum=sum*10+ch-'0';
            ch=getchar();
        }
        return sum*f;
    }
    signed main(){
        int a=read(),b=read(),k=read();
        if(k==1){
            printf("%lld",a*b-a-b);
            return 0;
        }
        priority_queue<int> q1;
        queue<int> q2;
        q1.push(a*b-a-b-a);
        q2.push(a*b-a-b-b);
        k--;
        int ans;
        while(k--){
            int w1=q1.top();
            int w2=q2.front();
            if(w1<w2){
                ans=w2;
                q2.pop();
                q2.push(w2-b);
                q1.push(w2-a);
            }
            else{
                ans=w1;
                q1.pop();
                q1.push(w1-a);
            }
        }
        printf("%lld",ans);
        return 0;
    }
    

    F 寻找宝藏


    按颜色考虑,把一种颜色的点去掉之后树会分成不同的连通块,对于一个大小为(a)的连通块,这个颜色对于这个连通块中点的答案贡献为(n-a)。设颜色数为(num)每一个点的答案就是(n*num-sum f(i)),其中(f(i))就是这个点在去掉同一颜色的点后所在连通块大小的和。

    然后就是实现上问题。

    怎么求连通块大小:(size(u)-sum size(v_i))

    怎么把连通块大小累加到所有在连通块中的点上:树上差分
    由于非根节点必然是去掉此节点父亲颜色后一个连通块的公共祖先。
    而根节点可能是很多连通块的公共祖先,写代码时要特判。

    TLE代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    #define N 1010000
    int cnt,a[N],num,n,pre[N],head[N],size[N],book[N],spe[N];
    long long sum[N],f[N];
    int read(){
    	int sum=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    struct edge{
    	int nxt,to;
    }e[N*2];
    void add_edge(int u,int v){
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dfs1(int u,int fa){
    	size[u]=1;
    	int tmp=pre[a[u]];
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(v==fa)continue;
    		pre[a[u]]=v;
    		dfs1(v,u);
    		size[u]+=size[v];
    	}
    	pre[a[u]]=tmp;
    	sum[pre[a[u]]]-=size[u];
    	spe[a[u]]-=size[u];
    	if(u==1) sum[u]+=(long long)size[u]*num;
    	else sum[u]+=size[u];
    }
    void dfs2(int u,int fa){
    	int tmp=pre[a[u]];
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(v==fa)continue;
    		pre[a[u]]=v;
    		dfs2(v,u);
    	}
    	pre[a[u]]=tmp;
    	if(u!=1){
    		if(pre[a[u]]==1)sum[u]-=spe[a[u]];
    		else sum[u]-=sum[pre[a[u]]];
    	}
    }
    void dfs3(int u,int fa,long long tot){
    	tot+=sum[u];
    	f[u]=tot;
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(v==fa)continue;
    		dfs3(v,u,tot);
    	}
    	tot-=sum[u];
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		book[a[i]]++;
    	}
    	for(int i=1;i<=n;i++)num+=(bool)book[i];
    	for(int i=1;i<=n-1;i++){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add_edge(u,v);
    		add_edge(v,u);
    	} 
    	for(int i=1;i<=n;i++)pre[a[i]]=1,spe[a[i]]=n;
    	dfs1(1,0);
    	dfs2(1,0);
    	dfs3(1,0,0);
    	for(int i=1;i<=n;i++)printf("%lld
    ",(long long)n*num-f[i]);
    	return 0;
    }
    
  • 相关阅读:
    欢迎使用CSDN-markdown编辑器
    欢迎使用CSDN-markdown编辑器
    Math类简介
    Math类简介
    http_server
    tcp服务器
    swoole安装
    laravel源码解析
    VMware的Unity模式
    string.format() %d越界的问题
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/14855090.html
Copyright © 2020-2023  润新知