2021杭电多校第一场

1001 Mod, Or and Everything

签到题吧。直接暴力打表,前100个数就看出来了。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

void solve()
{
    ll n;
    scanf("%lld",&n);
    if((n&(-n)) == n)
    {
        printf("%lld\n",max(0ll,n / 2 - 1));
        return ;
    }
    while((n&(-n)) != n) n -= (n & (-n));
    printf("%lld\n",n - 1);
    return ;
}


int main()
{
    ll t;
    scanf("%lld",&t);
    while(t -- )
    {
        solve();
    }
    return 0;
}

1005 Minimum spanning tree

比签到难一点点。2-n,只要是质数,就将它和2相乘,否则就是他本身,最后所有的加起来,预处理出所有的质数,线性筛法(好像也叫欧拉筛法,O(n)的吧,挺快的)。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e7 + 10;

int isprime[N];
bool st[N];
int cnt;

void solve1() // 找出1-N中所有的质数
{
    for(int i = 2;i < N;i ++ )
    {
        if(!st[i])
        {
            isprime[cnt ++] = i;
        }
        for(int j = 0;j < cnt && isprime[j] * i <= N;j ++ )
        {
            st[i * isprime[j]] = 1;
            if(i % isprime[j] == 0) break;
        }
    }
}

void solve(int n)
{
    ll res = 0;

    res += (ll)(n + 3) * (n - 2) / 2;

    for(int i = 1;i < cnt;i ++ )
    {
        if(isprime[i] <= n) res += isprime[i];
    }
    printf("%lld\n",res);
    //for(int i = 0;i < cnt;i ++ ) cout << isprime[i] << " \n"[i == cnt - 1];
}


int main()
{
    int t;
    cin >> t;
    solve1();
    while(t -- )
    {
        int n;
        scanf("%d",&n);
        solve(n);
    }
    return 0;
}

1006 Xor sum

01字典树。为了做这道题,学了一天的字典树,还去做了基础的练习,感觉理解了就简单了好多。维护前缀异或和,然后把他们插入01字典树,访问到叶子节点的时候,这串二进制数就代表当前的前缀异或和。依次访问,开一个id[]记录每个点最右边在哪,最后处理一下就行了。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, INF = 0x3f3f3f3f;

int tr[N*15][2],t,n,a[N],x,id[N*15],idx = 0,l,r,minn,k,len,b[N];

int res;

void insert( int pp , int x )
{
	int p = 0;
	for(int i=30;i>=0;i--)
	{
		if( !tr[p][ ((x>>i) & 1 )] )
		{
			idx++;
			tr[p][ ((x>>i) & 1 )] = idx;
		}
		p = tr[p][ ((x>>i) & 1 )];
		id[p] = pp;
	}
}

int search( int x )
{
	int p = 0;
	for(int i=30;i>=0;i--)
	{
		int s = ((x>>i) & 1);
		int kk = ((k >> i) & 1);
		if(kk == 0)
		{
		    if(tr[p][!s])
		    {
		        res = max(res,id[tr[p][!s]]);
		    }
		    p = tr[p][s];
		}
		else
		{
		    p = tr[p][!s];
		}
		if(p == 0) return -1;
	}
	return id[p];
}

void solve(  )
{
	memset(tr,0,sizeof(tr));
	memset(id,0,sizeof(id));
	len = INF;
	minn = INF;
	res = -1;
	l = -1,r = n+1;
	idx = 0;
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&b[i]);
		a[i] = b[i] ^ a[i-1];
	}
	insert( 0 , a[0] );
	for(int i=1;i<=n;i++)
	{
		int j = search( a[i] );
		int temp1 = b[i];
		if(j != -1)
		{
		    res = max(res,j);
		}
		j = res;
		int ll = j+1,rr = i;
		if( temp1>=k )
		{
			l = r = i;
			break;
		}
		else if( j != -1 && (a[j] ^ a[i]) >= k )
		{
			if( (rr-ll+1) < len )
			{
				l = ll;
				r = rr;
				len = r-l+1;
			}
			else if( (rr-ll+1) == len )
			{
				if( ll<l )
				{
					l = ll;
					r = rr;
					len = r-l+1;
				}
			}
		}
		insert( i , a[i] );
	}
	if( l==-1 )
	{
		printf("-1\n");
	}
	else printf("%d %d\n",l,r);
}

int main()
{
	//freopen("in.txt","r",stdin);
	cin>>t;
	while( t-- )
	{
		solve();
	}
	return 0;
}


1008 Maximal submatrix

直接转换成01矩阵,就是板子题了。可以用悬线法或者单调栈求最大01矩阵,其实这种方法还能求其他满足条件的子矩阵。可以看看这道题,似乎是19年银川的区域赛的题,学习的时候偶然发现的,就做了一下,挺简单的,也是先转换,然后用悬线法求,https://nanti.jisuanke.com/t/42391

#include<bits/stdc++.h>

using namespace std;

const int N = 2010;

int n,m;
int s[N][N],b[N][N];
int l[N],r[N],h[N];


int solve(int cur)
{
    for(int i = 1;i <= m;i ++ )
    {
        if(cur == 1) h[i] = 1;
        else 
        {
            if(b[cur][i] == 1 && b[cur - 1][i] == 1) h[i] = 1 + h[i];
            else if(b[cur][i] == 1 && b[cur - 1][i] == 0) h[i] = 2;
            else h[i] = 0;
        }
    }
    for(int i = 1;i <= m;i ++ )
    {
        l[i] = i;
        while(l[i] > 1 && b[cur][i] == 1 && (b[cur][l[i] - 1] == 1) && (h[l[i] - 1] >= h[i])) l[i] = l[l[i] - 1];
    }
    for(int i = m;i >= 1;i -- )
    {
        r[i] = i;
        while(r[i] < m && b[cur][i] == 1 && (b[cur][r[i] + 1] == 1) && (h[r[i] + 1] >= h[i])) r[i] = r[r[i] + 1];
    }
    int res = -1;
    for(int i = 1;i <= m;i ++ )
    {
            res = max(res,(r[i] - l[i] + 1) * h[i]);
    }
    //for(int i = 1;i <= m;i ++ ) cout << l[i] << " " << r[i] << " "<< h[i] << " \n"[i == m]; 
    return res;
}



int main()
{
    int t;
    cin >> t;
    while(t -- )
    {
        scanf("%d %d",&n,&m);
        for(int i = 1;i <= n;i ++ )
            for(int j = 1;j <= m;j ++ )
            {
                if(i == 1) b[i][j] = 1;
                scanf("%d",&s[i][j]);
            }
        for(int i = 2;i <= n;i ++ )
            for(int j = 1;j <= m;j ++ )
            if(s[i][j] >= s[i - 1][j]) b[i][j] = 1;
            else b[i][j] = 0;
            
        int res = m;
        
        for(int i = 1;i <= n;i ++ ) res = max(res,solve(i));
        printf("%d\n",res);
    }
    return 0;
    
}

1009 KD-Graph

感觉就是最小生成树,克鲁斯卡尔算法的感觉,定义为签到题。。。先对所有边排个序,每次取出最小的边,判断该边的两个点是否在一个集合,如果不在,则将他们放入一个集合(并查集实现),每次再判断一下当前的集合是不是等于题目要求的集合数量。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int n,m,k;

struct node
{
    int u,v,c;
}a[N];

int p[N];


bool cmp(node a,node b)
{
    return a.c < b.c;
}

int fin(int x)
{
    if(p[x] != x) p[x] = fin(p[x]);
    return p[x];
}


void solve()
{
    scanf("%d %d %d",&n,&m,&k);
    for(int i = 1;i <= n;i ++ ) p[i] = i;
    for(int i = 1;i <= m;i ++ )
    {
        int u,v,c;
        scanf("%d %d %d",&u,&v,&c);
        a[i] = {u,v,c};
    }
    sort(a + 1,a + m + 1,cmp);
    int now = n;
    int res = 0;
    for(int i = 1;i <= m;i ++ )
    {
        if(a[i].c != a[i - 1].c)
        {
            if(now == k)
            {
                printf("%d\n",res);
                return ;
            }
        }
        if(fin(a[i].u) == fin(a[i].v)) continue;
        now --;
        p[fin(a[i].u)] = fin(a[i].v);
        res = a[i].c;
    }
    printf("%d\n",now == k ? res : -1);
}


int main()
{
    int t;
    cin >> t;
    while(t -- )
    {
        solve();
    }
    return 0;
}


1010 zoto

莫队加树状数组。之前莫队都不知道是啥,也是吐了花了一天搞理论,然后也做了几道基础题才把这道搞出来。莫队懂了原理还是很简单的,就是一个优化版的暴力,核心思想就是分块和移动,一般分块大小就是更号n,记得有一个什么奇偶分块,可以加快查找速度,后面再去补一下。用莫队处理m次询问,然后他要询问给定y轴区间内不同的点的个数,立马就想到了树状数组,可能是逆序对做多了的原因吧。莫队每次移动的时候要维护一下当前横轴区间每个数出现的次数,加入到树状数组里面去,就可以了。最后莫队的时间复杂度后面再去看看证明,感觉很神奇的样子。。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n,m;
int a[N];
int cnt[N];
int pos[N];
int tr[4 * N];

struct node
{
    int l,r,up,down;
    int id;
    int res;
}query[N];

int lowbit(int x)
{
    return x & (-x);
}

bool cmp1(node a,node b)
{
    if(pos[a.l] != pos[b.l]) return a.l < b.l;
    else return a.r < b.r;
}

bool cmp2(node a,node b)
{
    return a.id < b.id;
}

void update(int x,int c)
{
    for(int i = x;i <= n;i += lowbit(i)) tr[i] += c;
}

int sum(int x)
{
    int ans = 0;
    for(int i = x;i > 0;i -= lowbit(i)) ans += tr[i];
    return ans;
}

void add(int i)
{
    if(!cnt[a[i]]) update(a[i],1);
    cnt[a[i]] ++;
}

void del(int i)
{
    cnt[a[i]] --;
    if(!cnt[a[i]]) update(a[i],-1);
}


void solve()
{
    for(int i = 0;i < 4 * N;i ++ ) tr[i] = 0;
    scanf("%d %d",&n,&m);
    int block = sqrt(n);
    for(int i = 1;i <= n;i ++ ) scanf("%d",&a[i]), a[i] ++, cnt[i] = 0,pos[i] = i / block;
    for(int i = 1;i <= m;i ++ )
    {
        int x1,y1,x2,y2;
        scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
        query[i].l = x1, query[i].r = x2, query[i].up = ++ y2,query[i].down = ++ y1;
        query[i].id = i;
    }
    sort(query + 1,query + 1 + m,cmp1);
    int l = 0, r = 0;
    for(int i = 1;i <= m;i ++ )
    {
        int ql = query[i].l, qr = query[i].r;
        while(l < ql) del(l ++);
        while(l > ql) add(-- l);
        while(r < qr) add(++ r);
        while(r > qr) del(r --);
        query[i].res = sum(query[i].up) - sum(query[i].down - 1);
        //cout << sum(query[i].up) << " " << sum(query[i].down - 1) << endl;
    }

    sort(query + 1,query + 1 + m,cmp2);
    for(int i = 1;i <= m;i ++ )
        cout << query[i].res << endl;
}



int main()
{
    int t;
    cin >> t;
    while(t -- )
    {
        solve();
    }
    return 0;
}

1007 Pass!

额,至于这道题我是真没看懂,递推公式还能勉强看懂,bsgs算法还没看懂,后面慢慢看吧,先把代码贴上。

#include<bits/stdc++.h>
using namespace std;
#define N 10000010
#define LL long long
#define MOD 998244353
LL T,n,x,base,B=sqrt(MOD);
unordered_map<LL,LL>mp;
LL qow(LL x,LL y){return y?(y&1?x*qow(x,y-1)%MOD:qow(x*x%MOD,y/2)):1;}
int main()
{
	scanf("%lld",&T);
	while(T--){
		scanf("%lld%lld",&n,&x);
		x=x*n%MOD*qow(n-1,MOD-2)%MOD;
		mp.clear();
		mp[1]=0;
		base=(n-1)*(n-1)%MOD;
		LL f1=(x+MOD-1)*(n-1)%MOD;
		LL f2=(x+1)%MOD;
		if(f1==1){
			printf("0\n");
			continue;
		}
		if(f2==1){
			printf("1\n");
			continue;
		}
		LL p=1,flg=0;
		for(LL i=1;i<=B;i++){
			p=p*base%MOD;
			mp[p]=i;
			if(f1==p){
				printf("%lld\n",2ll*i);
				flg=1;
				break;
			}
			if(f2==p){
				printf("%lld\n",2ll*i+1);
				flg=1;
				break;
			}
		}
		if(flg)continue;
		p=qow(p,MOD-2);
		for(LL k=1,kp=p;k*B<MOD;k++,kp=kp*p%MOD){
			if(mp.count(f1*kp%MOD)){
				printf("%lld\n",2ll*(k*B+mp[f1*kp%MOD]));
				flg=1;
				break;
			}
			if(mp.count(f2*kp%MOD)){
				printf("%lld\n",2ll*(k*B+mp[f2*kp%MOD])+1);
				flg=1;
				break;
			}
		}
		if(flg)continue;
		printf("-1\n");
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值