2021“MINIEYE杯”(1)

Start Time : 2021-07-20 12:10:00 End Time : 2021-07-20 17:10:00

1001-Mod, Or and Everything

真正的签到题
题意: 给定一个整数 n。计算 (n mod 1) | (n mod 2) | ... | (n mod (n - 1)) | (n mod n)。(|为"或运算")

由于是或运算,找到最大的数(在n/2和n/2+1中找),得到二进制中最多位数m,结果就是用1填满这些位数,即2^m-1(m为最大数二进制的位数)

#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>

#define ri  int

using namespace std;
typedef int lll;
typedef long long ll;

const ll mod=1e9+7;
inline ll gcd(ll a,ll b) {
    return (!b)?a:gcd(b,a%b);
}
const ll inf=999999999;


ll t;
ll n;

ll finds(ll x)
{
    ll f=1;
    while(f<=x)
    {
        f=f*2;
    }
    return f-1;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    
    cin >> t;
    while(t--)
    {
        cin >> n;
        if(n==1||n==2) cout << 0 << '\n';
        else if(n==3) cout << 1 << '\n';
        else
        {
            ll n1=n/2,n2=n1+1;
            n1=n%n1,n2=n%n2;
            ll ans=n1 > n2 ? n1 : n2;
            cout << finds(ans) << '\n';
        }    
    }    
    return 0;
}

1005-Minimum spanning tree

题意: 给定 n-1 个点,编号从 2 到 n,两点 a 和 b 之间的边权重为 lcm(a, b)。 请找出它们形成的最小生成树。

简单画成哈斯图就知道规律了,是质数就插在2上,其他插在自己因子上,故质数*2+合数,数据大用线性筛找质数

#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>

#define ri  int

using namespace std;
typedef int lll;
typedef long long ll;

const ll mod=1e9+7;
inline ll gcd(ll a,ll b) {
    return (!b)?a:gcd(b,a%b);
}
const ll inf=999999999;
const ll N = 10000000+10;
vector<ll> vis(N);
vector<ll> prime(N);

ll k=0;


void isprime() 
{
    for(ri i=2;i<N;i++)
    {
        if(!vis[i]) prime[++k]=i;
        for(ri j=1;j<=k&&(i*prime[j]<N);j++)
        {
            vis[i*prime[j]]=1;
            if(!i%prime[j]) break;
        }
    }
}
ll t;
ll n;


int main() {
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    
    isprime();
    vector<ll> dp(N);
    dp[2]=0,dp[3]=6;
    ll now=3;
    for(ri i=4;i<=N-5;i++)
    {
        if(prime[now]==i) 
        {
            dp[i]=dp[i-1]+(2*i);
            now++;
        }
        else dp[i]=dp[i-1]+i;
    }
    cin >> t;
    while(t--)
    {
        cin >> n;
        cout << dp[n] << '\n';
    }    
    return 0;
}

1006-Xor sum

题意:
给定一个长度为 n 的整数序列,找出最短的连续子序列,其异或和不小于 k。
如果有多个相同长度的连续子序列,则打印左端点最小的连续子序列。
如果没有连续的子序列,异或和不小于k,就打印“-1”。

第一次做这种XOR序列的题目,记录一下。主要用到 XOR前缀01trie树 两种重要的优化手段

XOR(a[l..r]) = XOR(a[1..r]) xor XOR(a[1..l-1]) ,并且 a=a^b ^ b ,b= a^b ^ a

区间异或和可以转化为区间端点之间的xor ,即找ai,aj(i<j),使ai ^ aj >=k,且min(j-i+1)。

其中01trie树可以在logn复杂度在 某些数 中找到能与 某个数 取最大异或结果那个数

故从右R开始,把1...R-1看成 某些数 ,R看成 某个数 ,找最大的xor结果,如果结果>=k,检查区间长度并更新即可(R从1开始增长就好)

#include<bits/stdc++.h>
#define ri int
using namespace std;
typedef long long lll;
typedef int ll;

const ll N=1e5+50;
ll t[N*31+10][2];
ll val[N],a[N];
ll cnt,n,k,T;

void _init()  //多组输入注意清空树
{
    for(ri i=0;i<=cnt;i++)
    {
	t[i][0]=t[i][1]=val[i]=0;
    }
    cnt=0;
}

void add(ll x,ll pos)  //把x插入trie树中,val记录下标
{
    ll now=0,y=0;
    for(ri i=31;i>=0;i--)
    {
	y=(x >> i) & 1;
	if(!t[now][y]) t[now][y]=++cnt;
	now=t[now][y];
    }
    val[now]=pos;
}

ll ask(ll x)   //查找最大符合的数 
{
    ll now=0,y=0;
    for(ri i=31;i>=0;i--)
    {
	y=(x >> i) & 1;
	if(t[now][y ^ 1]) now=t[now][y ^ 1];
	else now=t[now][y];
    }	
    return val[now];  //同样返回下标
} 


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
	
    cin >> T;
    while(T--)
    {
	_init();
	cin >> n >> k;
	for(ri i=1;i<=n;i++)
	{
	    cin >> a[i];
	    a[i]^=a[i-1];  //前缀XOR和 
	}
	ll l=-1,r=n+1,len=n+1;
		
	for(ri i=1;i<=n;i++)   //R从1开始增长即可
	{
	    ll R=i,L=ask(a[i]);
	    if((a[R]^a[L])>=k)  //如果>=k
	    {
		if(R-L<len) len=R-L,r=R,l=L+1;  //判断是否需要更新
	    }
	    add(a[i],i);
	}
		
	l==-1 ? cout << l << '\n' : cout << l << ' ' << r << '\n';  //一次都没有更新则不存在
    }
	
    return 0;
}

1008-Maximal submatrix

题意: 给出一个N * M矩阵,求出最大的列上升子矩阵.

将矩阵化成0-1矩阵,压缩成一维求一维柱状体最大面积,由于怕全为0卡数据,转化后将所有0的变为1即可

#include<bits/stdc++.h>
#define ri int
using namespace std;
typedef long long lll;
typedef int ll;

ll n,m;

ll a[2005][2005];
ll mp[2005][2005];


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
	    for(int j=1;j<=m;j++)
	    {
	        cin>>a[i][j];
	        mp[i][j]=(mp[i-1][j]+1)*(a[i][j]>=a[i-1][j]);
	        if(!mp[i][j]) mp[i][j]=1;
	    }
	 }
	       
	 ll stk[3000],tt=0;
	 ll maxx=0;
	 for(ri i=1;i<=n;i++)
	 {
	     mp[i][m+1]=-1,tt=0;
	     for(ri j=1;j<=m+1;j++)
	     {
	    	if(!tt||mp[i][j]>=mp[i][stk[tt]]) stk[++tt]=j;
	    	else
	    	{
	    	    ll now=0,top=0;
	    	    while(tt&&mp[i][j]<mp[i][stk[tt]])
	    	    {
	    		top=stk[tt--];
	    		now=(j-top)*mp[i][top];
	    		maxx=max(now,maxx);
		    }
		    stk[++tt]=top;
		    mp[i][top]=mp[i][j];
		 }
	      }
          }
	cout << maxx << '\n';
    }
       
    return 0;
}

1009-KD-Graph

签到题是图论还没做出,大大滴有问题(主要是题目都没看

题目大意:是要我们找到一个集合内所有边的权值小于等于某个数D,剩下的集合权值都大于某个数D,并且整体集合数为K.

思路 :先按照边的权值排序,从小往大合并集合,当集合数达到K个数时,更新D的值,并判断下一个边的权值大小和此时D的大小关系即可。

输入:n(顶点个数),m(边数),k(要求集合个数)
m组:起点x,终点y,权值z

输出:题目中要求的某个数D,若找不到输出“-1”

代码如下:

#include <bits/stdc++.h>
#define ri  int

typedef int lll;
typedef long long ll;
using namespace std;

const ll mod=80112002;
const ll inf=999999999;

const ll N=5e5+5;

ll n,m,k;
struct dd{
	ll x,y,z;
}a[N];

ll t;
bool cmp(dd a,dd b)
{
    return a.z<b.z;
}

ll p[N];
ll getp(ll x)   //集合合并当然是并查集
{
    return p[x]==x ? x : p[x]=getp(p[x]);
}

int main()
{   
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    
    cin >> t;
    while(t--)
    {
    	cin >> n >> m >> k;
    	for(ri i=1;i<=n;i++) p[i]=i;
    	for(ri i=1;i<=m;i++) cin >> a[i].x >> a[i].y >> a[i].z;
    	if(n==k)       //特判
    	{
    	    cout << 0 << '\n';
    	    continue;
	}
    	sort(a+1,a+1+m,cmp);
    	
    	ll nums=n;  //当前集合个数number_set
    	ll ans=-1;
    	ll px,py;
    	for(ri i=1;i<=m;i++)
    	{
    	    px=getp(a[i].x),py=getp(a[i].y);
    	    if(px==py) continue;  //如果本身是同一个集合则不用考虑
    		
    	    if(nums-1==k) //集合数为k时更新
    	    {
    		ans=a[i].z;
	    }
	    else if(nums-1<k) //为k后判断下一条边
	    {
		if(ans>=a[i].z) ans=-1;  //找不到D
		break;
	    }
		p[px]=py;nums--;
	}
	    if(ans==-1) cout << "-1\n";
	    else cout << ans << '\n';
    }
    return 0;
}
posted @ 2021-07-21 14:57  gonghw403  阅读(53)  评论(0编辑  收藏  举报