2021杭电多校1补题记录

总结

carry不在的第一场杭电
开局我读到去年网络赛原题,臭不要脸的签上到了。赛后发现结论也是显然。
郎老师读1001极速签到,又是飞快签到的一天。
1009签到题但没什么人做。。因为一上来1005是个最小生成树,导致我一度看什么都是最小生成树,写了个二分最小生成树TLE了,发下二分+搜索就可以做了,居然,还能wa!。不过也是很少写这种二分,赛后STD也不是二分…还是水平薄弱,应该检讨。
1008郎老师提供的思路,我跟着完善了下,郎老师交了一发a了。赛后知道有更简单的操作,于是补习了一下悬线法,只看没实现过的单调栈也写了下。
1006属于是知识盲区了,赛后lls和我都补了,新技能get。

1001 题意

给n,求 (n mod 1) or (n mod 2) or … or (n mod (n - 1)) or (n mod n).

1001 思路

因为是or,我们只要考虑每一位的1即可。容易发现n-2,n-4,n-8…n-2^ k是几组特别的模数,当n-2^ k>2^ k时,可以保证答案是2^ (k+1)-1。打一下2的幂的表,二分即可。

1001 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    vector<int>v;
	void solve(){
        cin>>n;
        if(n<3){
            cout<<0<<endl;
            return ;
        }
        auto p=lower_bound(v.begin(),v.end(),n);
        while(*p>=n)p--;
        cout<<*p-1<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
        int now=1;
        v.push_back(0);
        v.push_back(1);
        for(int i=1;i<=60;i++){
            v.push_back(2*now);
            now*=2;
        }
		while(tn--){
			solve();
		}
	} 
	
						
1005 题意

2-n的点,相互之间有lcm(a,b)的边,最小生成树。

1005 思路

数学归纳法思想,第一个点是2,这时代价是0。假设我们已经完成了前n个,考虑第n+1个点。前面n个点显然是联通的,那么我们随便连一个代价最小的边就行了。当n+1是质数时,我们连2,否则,连他一个约数即可。

答案就是3到n的2倍质数加全部合数。可以线性筛一下质数,求个前缀和,二分加一个等差求和即可。

1005 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=10000005;
	const int inf=0x3f3f3f3f;
	int n,m,k,q;
	vector<int>prime;
	int pri[maxn],sum[maxn];
	int cnt;
    int v[maxn];
    void Euler_prime(int n){
        memset(v,0,sizeof v);
        for(int i=2;i<=n;i++){
            if(!v[i]){
                v[i]=i;
				pri[++cnt]=i;
            }
            for(int w=1;w<=cnt;w++){
				int j=pri[w];
                if(j>v[i]||j>n/i)   break;
                v[i*j]=j;
            }
        }
		for(int i=1;i<=cnt;i++)
			sum[i]=sum[i-1]+pri[i];
	}
	void solve(){
		cin>>n;
		if(n<=2){
			cout<<0<<endl;
			return ;
		}
		int pos=lower_bound(pri+1,pri+1+cnt,n)-pri;
		if(pri[pos]>n)	pos--;
		cout<<sum[pos]+(n*n+n-10)/2<<endl;

	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
		Euler_prime(10000003);
		while(tn--){
			solve();
		}
	} 
						
1006 题意

给你一个数组,求最短且最靠左的连续区间,使其异或和大于等于k

1006 思路

首先前置芝士,异或和可以前缀和,这样连续区间可以转化为在前缀异或和数组上找两个最近的数,异或大于等于k。

直接枚举n²,这里用到了01trie树,就是把一个数字当成二进制串,放在trie上维护,它可以nlogn的求出两个数异或最大值。

我们类似的维护trie树,同时开一个index数组保留这个trie树上结点对应的最靠右结点下标。每次先搜索当前数字能否在树上异或出大于k的,如果可以,最靠右的下标是谁,之后更新答案,再把当前数插入。注意前缀和数组,所以左边下标需要加1处理。

1006 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=100505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int a[maxn];
    int trie[maxn*31+10][2];
    int index[maxn*31+10];
    int tot=1;
    void insert(int x,int in){
        int p=1;
        for(int k=30;~k;k--){
            int ch=(x>>k)&1;
            if(!trie[p][ch]){
                trie[p][ch]=++tot;
                trie[tot][0]=trie[tot][1]=0;
                index[tot]=-1;
            }
            p=trie[p][ch];
            index[p]=max(in,index[p]);
        }
    }
    int find(int x){
        int p=1;
        int ret=0;
        for(int k=30;~k;k--){
            int ch=(x>>k)&1;
            int t=(m>>k)&1;
            if(trie[p][ch^1]){
                p=trie[p][ch^1];
                ret |= 1<<k;
                if(ret>=m){
                    return index[p];
                }
            }
            else{
                p=trie[p][ch];
            }
        }
        return ret>=m?index[p]:-1;
    }
	void solve(){
        cin>>n>>m;
        a[0]=0;tot=1;
        trie[1][0]=trie[1][1]=0;
        index[1]=-1;
        int l,r,mi=inf;
        bool ok=0;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            if(!ok&&a[i]>=m){
                ok=1;
                l=r=i;
            }
            a[i]^=a[i-1];
        }
        if(ok){
            cout<<l<<' '<<r<<endl;
            return ;
        }
        for(int i=1;i<=n;i++){
            int t=find(a[i]);
            if(~t&&i-t<mi){
                mi=i-t,r=i,l=t;
            }
            insert(a[i],i);
        }
        if(mi==inf) cout<<-1<<endl;
        else cout<<l+1<<' '<<r<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
1008 题意

二维矩阵,问满足每一列上到下递增的最大子矩阵。nm算法可过

1008 思路

预处理k矩阵,代表每个元素向下拓展保持递增的最长长度。可以用栈在nm的复杂度处理出来。
之后就是遍历每行,相当于对ki数组进行一个最大直方图面积的处理。悬线法或者单调栈可以O(m)处理,总得复杂度O(nm)。下附两个版本

1008 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=2005;
	const int inf=0x3f3f3f3f;
	int n,m;
    int vis[maxn];
    static char buf[100000],*pa=buf,*pd=buf;
    #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
    inline int read(){
        register int x(0);register char c(gc);
        while(c<'0'||c>'9')c=gc;
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
        return x;
    }
    int a[maxn][maxn],k[maxn][maxn];
    int l[maxn],r[maxn];
	void solve(){
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)   
                a[i][j]=read();
        for(int j=1;j<=m;j++){
            stack<int>s;
            for(int i=1;i<=n;i++){
                if(s.empty()||a[i][j]>=a[s.top()][j]) s.push(i);
                else{
                    while(s.size()){
                        k[s.top()][j]=i-1;
                        s.pop();
                    }
                    s.push(i);
                }
            }
            while(s.size()){
                k[s.top()][j]=n;
                s.pop();
            }
            for(int i=1;i<=n;i++)   
                k[i][j]-=i-1;
        }
        //for(int i=1;i<=n;i++){
        //    for(int j=1;j<=m;j++)
        //        cout<<k[i][j]<<' ';
        //    cout<<endl;
        //}
        int ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)
                l[j]=r[j]=j;
            for(int j=1;j<=m;j++)
                while(l[j]>1&&k[i][l[j]-1]>=k[i][j])    l[j]=l[l[j]-1];
            for(int j=m;j;j--)
                while(r[j]<m&&k[i][r[j]+1]>=k[i][j])    r[j]=r[r[j]+1];
            for(int j=1;j<=m;j++){
                ans=max(ans,k[i][j]*(r[j]-l[j]+1));
            }
        }
        cout<<ans<<endl;
	}
	signed main(){
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		tn=read();
		while(tn--){
			solve();
		}
	} 
	
						
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=2005;
	const int inf=0x3f3f3f3f;
	int n,m;
    int vis[maxn];
    static char buf[100000],*pa=buf,*pd=buf;
    #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
    inline int read(){
        register int x(0);register char c(gc);
        while(c<'0'||c>'9')c=gc;
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
        return x;
    }
    int a[maxn][maxn],k[maxn][maxn];
	void solve(){
        n=read(),m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)   
                a[i][j]=read();
        for(int j=1;j<=m;j++){
            stack<int>s;
            for(int i=1;i<=n;i++){
                if(s.empty()||a[i][j]>=a[s.top()][j]) s.push(i);
                else{
                    while(s.size()){
                        k[s.top()][j]=i-1;
                        s.pop();
                    }
                    s.push(i);
                }
            }
            while(s.size()){
                k[s.top()][j]=n;
                s.pop();
            }
            for(int i=1;i<=n;i++)   
                k[i][j]-=i-1;
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            k[i][m+1]=0;
            stack<pair<int,int> >s;
            for(int j=1;j<=m+1;j++){
                int w=0;
                while(s.size()&&k[i][j]<=s.top().first){
                    w+=s.top().second;
                    ans=max(ans,s.top().first*w);
                    s.pop();
                }
                s.push({k[i][j],1+w});
            }
        }
        cout<<ans<<endl;
	}
	signed main(){
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		tn=read();
		while(tn--){
			solve();
		}
	} 
	
						
1009 题意

n点分成k组,要求同组必须至少一条路上最长边比d小,不同组不能有任何一条路最长边比d小,问最小化d。

1009 思路

官方题解是边排序,维护当前d值,每次将新的权值一样的边全部链接上,如果联通块等于k个,就是有解。

比赛时整的二分,二分d看能不能连成k个块。

1009 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=500505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int vis[maxn];
    static char buf[100000],*pa=buf,*pd=buf;
    #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
    inline int read(){
        register int x(0);register char c(gc);
        while(c<'0'||c>'9')c=gc;
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
        return x;
    }
    int ans=inf;
    vector<pair<int,int>>e[maxn];
    void init(){
        for(int i=1;i<=n;i++)
            e[i].clear();
        ans=inf;
    }

    int now;
    void dfs(int x){
        for(auto p:e[x]){
            int y=p.first,w=p.second;
            if(w<=now&&vis[y]!=now){
                vis[y]=now;
                dfs(y);
            }
        }
    }
    int check(int x){
        int tot=0;
        now=x;
        for(int i=0;i<=n;i++)   vis[i]=-1;
        for(int i=1;i<=n;i++){
            if(vis[i]!=x){
                vis[i]=x;
                tot++;
                dfs(i);
            }
        }
        if(k==tot)  ans=min(ans,x);
        return tot;
    }
	void solve(){
        n=read(),m=read(),k=read();
        init();
        int ma=0;
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),w=read();
            e[u].push_back({v,w});
            e[v].push_back({u,w});
            ma=max(w,ma);
        }      
        if(k==n){
            printf("0\n");
            return;
        }
        int l=0,r=ma+10,mid;
        while(l<r){
            mid=(l+r)/2;
            //cout<<mid<<endl;
            int t=check(mid);
            if(t<=k){
                r=mid;
            }
            else{
                l=mid+1;
            }
        }
        if(ans==inf)    ans=-1;
        printf("%d\n",ans);
	}
	signed main(){
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		tn=read();
		while(tn--){
			solve();
		}
	} 
	
						
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
//#define int long long
//#define double long double
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
	typedef long long ll;
	const int maxn=500505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int fa[maxn];
    int find(int x){
        return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
    }
    void init(){
        for(int i=1;i<=n;i++)   fa[i]=i;
    }
    struct Edge{
        int u,v,w;
    }edge[maxn];
    bool cmp(Edge a,Edge b){
        return a.w<b.w;
    }
	void solve(){
        cin>>n>>m>>k;
        init();
        for(int i=1;i<=m;i++){
            cin>>edge[i].u>>edge[i].v>>edge[i].w;
        }      
        if(k==n){
            cout<<0<<endl;
            return;
        }
        sort(edge+1,edge+1+m,cmp);
        edge[m+1]={0,0,inf};
        int last=-1;
        int cnt=n;
        for(int i=1;i<=m+1;i++){
            if(edge[i].w==last){
               int fx=find(edge[i].u),fy=find(edge[i].v);
                if(fx!=fy){
                    cnt--;
                    fa[fx]=fy;                    
                } 
            }
            else{
                if(cnt<k){
                    cout<<-1<<endl;
                    return ;
                }
                else if(cnt==k){
                    cout<<last<<endl;
                    return ;
                }
                last=edge[i].w;
                i--;
            }
        }
        cout<<-1<<endl;
	}
	signed main(){
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
        IOS
		int tn=1;
		cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
1010 题意

给出数列,每次询问l到r上low到high上有多少个位置有数字。

1010 思路

想着想着就忘了一个x只对应一个y了。
横坐标莫队,纵坐标分块。
莫队的指针移动是O(1)的,满足莫队使用条件。
莫队的移动是msqrt n,对应分块修改o(1)
莫队的查询是m,对应分块查询是O(sqrt m)
所以总的复杂度也是m根号m这个级别的,1e5不卡能过

1010 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=100505;
    const int maxm=1005;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int len;
    int a[maxn],id[maxn];
    static char buf[100000],*pa=buf,*pd=buf;
    #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
    inline int read(){
        register int x(0);register char c(gc);
        while(c<'0'||c>'9')c=gc;
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
        return x;
    }
    int ans[maxn];
    int cnt[maxn];
    int num[maxn];
    int low,high;
    void add(int x){
        //cout<<cnt[a[x]]<<endl;
		if(!cnt[a[x]])
            num[id[a[x]]]++;//,cout<<1<<endl;
		cnt[a[x]]++;
	}
	void del(int x){
        //cout<<cnt[a[x]]<<endl;
		cnt[a[x]]--;
        if(!cnt[a[x]])
            num[id[a[x]]]--;//,cout<<1<<endl;
	}
    struct Q{
        int l,r,low,high;
        int id;
    }q[maxn];
    bool cmp(Q A,Q B){
        return (id[A.l] ^ id[B.l]) ? id[A.l] < id[B.l] : ((id[A.l] & 1) ? A.r < B.r : A.r > B.r);
	}
    int query(int x,int y){
        int ret=0;
        if(id[x]==id[y]){
            for(int i=x;i<=y;i++)
                if(cnt[i])  ret++;
            return ret;
        }
        for(int i=x;id[i]==id[x];i++){
            if(cnt[i])  ret++;
        }
        for(int i=id[x]+1;i<id[y];i++)
            ret+=num[i];
        for(int i=y;id[i]==id[y];i--){
            if(cnt[i])  ret++;
        }
        return ret;
    }
    signed main(){
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
        tn=read();
        len=sqrt(maxn);
        for(int i=0;i<maxn;i++)
            id[i]=(i-1)/len+1;
        while(tn--){
            n=read(),m=read();
            memset(cnt,0,sizeof cnt);
            memset(num,0,sizeof num);
            for(int i=1;i<=n;i++) a[i]=read();
            
            for(int i=1;i<=m;i++){
                int x1=read(),y1=read(),x2=read(),y2=read();
                q[i]={x1,x2,y1,y2,i};
            }
            sort(q+1,q+1+m,cmp);
            int l=1,r=0;
            for(int i=1;i<=m;i++){
                int ll=q[i].l,rr=q[i].r;
                low=q[i].low,high=q[i].high;
                while(r<rr)	add(++r);
                while(l>ll) add(--l);
                while(r>rr)	del(r--);
                while(l<ll)	del(l++);
                ans[q[i].id]=query(low,high);
            }
            for(int i=1;i<=m;i++)	
                printf("%d\n",ans[i]);
        }
	} 
						
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值