2021“MINIEYE杯”中国大学生算法设计超级联赛(1)

1001 Mod, Or and Everything

两个for循环打表,总结结果

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

ll pow1(ll a,ll b){ll r=1;while(b){if(b&1)r=r*a;a=a*a;b>>=1;}return r;}

ll t,n;
int main()
{
//    for(int i=1;i<=1000;i++){
//        int x=0;
//        for(int j=1;j<=i;j++)
//            x|=i%j;
//        cout<<x<<' ';
//    }

    cin>>t;
    while(t--){
        cin>>n;
        ll k=2,cnt=0;
        while(k<n) ++cnt,k*=2;
        cout<<pow1(2,cnt)-1<<'\n';
    }
}


1005 Minimum spanning tree

当一个数为质数时与2相连,贡献是这个数的两倍,为合数时与他的其中一个质因子相连,贡献为这个数本身,预处理出质数和结果

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e6+5;

const int D=1e7+5,PD=7e5+5;
ll cnt,p[PD],t,n;
ll a[D];
bool np[D];
int main()
{
	//欧拉素数筛
    for(int i=2;i<D;i++){
        if(!np[i]) p[++cnt]=i;
        for(int j=1;j<=cnt&&p[j]*i<D;j++){
            np[i*p[j]]=1;
            if(i%p[j]==0) break;
        }
    }
    a[2]=0;
    int k=2;
    for(ll i=3;i<D;i++){
        if(p[k]==i){
            a[i]=a[i-1]+i*2;
            k++;
        }
        else
            a[i]=a[i-1]+i;
    }
    cin>>t;
    while(t--){
        cin>>n;
        cout<<a[n]<<'\n';
    }
}

1006 Xor sum

前置知识
在这里插入图片描述
可以将打印的注释和IOS去掉,观察变化

#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
#define dep(i,x,y) for(auto i=(x);i>=(y);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;
int t,n,k;
int a[N],p[N*30][2],mx[N*30];//p是字典树,mx是记录当前结点的最右边的位置
int main()
{
    IOS;
    cin>>t;while(t--){
        cin>>n>>k;
        rep(i,1,n) cin>>a[i],a[i]^=a[i-1];
        int l=-1,r=n,cnt=1; //l为左边界,为右边界
        mx[1]=-1;
        p[1][0]=p[1][1]=0;

        rep(i,0,n){
            int x=1,lp=-1; //lp表示左边界
            dep(j,29,0){
                int w=(a[i]>>j)&1;
                if(!((k>>j)&1)){
                    if(p[x][w^1]) lp=max(mx[p[x][w^1]],lp);//k的这位是0,那么w这位要异或与他本身的不一样的才能使结果大于k
                    x=p[x][w];
                }
                else x=p[x][w^1];//k的这位是1,则一定要异或不一样的
                if(!x) break; //x==0时,说明没有这个数
            }
            if(x) lp=max(mx[x],lp);
            if(lp>=0&&i-lp<r-l) r=i,l=lp;

            x=1;
//            cout<<"a["<<i<<"]="<<a[i]<<'\n';
            dep(j,29,0){
                int w=(a[i]>>j)&1;
                if(!p[x][w]){
                    p[x][w]=++cnt;
                    p[cnt][0]=p[cnt][1]=0;
                    mx[cnt]=-1;
                }
                x=p[x][w];
                mx[x]=i;
//                cout<<"p0"<<" ";for(int i=1;i<=40;i++) printf("%2d",p[i][0]),cout<<" ";cout<<'\n';
//                cout<<"p1"<<" ";for(int i=1;i<=40;i++) printf("%2d",p[i][1]),cout<<" ";cout<<"\n";
//                cout<<"mx"<<" ";for(int i=1;i<=40;i++) printf("%2d",mx[i]),cout<<" ";cout<<"\n\n";
            }
        }
        l>=0?cout<<l+1<<" "<<r<<'\n':cout<<-1<<'\n';
    }
}

1007 Pass!

f(t)=(n-2)f(t-1)+(n-1)f(t-2)
特征方程 x^2-(n-2)x-(n-1)=0
特征根 x1=n-1 x2=-1
特征方程f(t)=A(n-1)^ t + B(-1)^t
已知 f(0)=1,f(1)=0
则A+B=1,A(n-1)-B=0,
A=1/n,B=(n-1)/n
f(t)=((n-1)t+(n-1)(-1)t)/n
分奇偶
f(t)=((n-1)t-(n-1))/n (n为奇)
f(t)=((n-1)t+(n-1))/n (n为偶)
移项
z1=nf(t)+(n-1)=(n-1)t (n为奇)
z2=nf(t)-(n-1)=(n-1)t (n为偶)

已知y=n-1,z如上,p=998244353,求t
用BSGS算法
yt≡z(mod)p
当gcd(y,p)=1时
设 t=ax-b则 y(ax-b)≡z(mod p) (a,b∈[0,√p))
y ax≡z*yb (mod p)
inv(z)*y ax≡yb (mod p)

暴力枚举 b,用unordered_map存<yb,b>
暴力枚举a, 找到map[inv(z)*y ax]对应的数

#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
#define dep(i,x,y) for(auto i=(x);i>=(y);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e6+5;
const ll MOD=998244353;

ll pow2(ll a,ll b,ll mod){ll r=1;while(b){if(b&1)r=r*a%mod;a=a*a%mod;b>>=1;}return r%mod;}
void exgcd(ll a, ll b, ll &d, ll &x, ll &y){if(!b) { d = a; x = 1; y = 0;}else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }}
ll inv(ll a){ll d, x, y;exgcd(a, MOD, d, x, y);return d == 1 ? (x + MOD) % MOD : -1;}

ll p,y,z1,z2,t,n,k;
unordered_map<ll,ll>mp;
int main()
{
    scanf("%lld",&t);
    while(t--){
        mp.clear();
        scanf("%lld%lld",&n,&k);
        if(k==0){cout<<1<<'\n';continue;}
        if(k==1){cout<<0<<'\n';continue;}

        p=MOD;
        y=n-1;

        z1=inv((k*n+n-1)%p);//奇数
        z2=inv((k*n-n+1)%p);//偶数


        ll b=(ll)sqrt(p);
        ll x=y,yy=1;
        rep(i,1,b){
            mp[x]=i;
            x=x*y%p;
        }

        x=pow2(y,b,p);
        rep(i,0,b){
            ll p1=yy;
            yy=yy*x%p;

            if(mp[p1*z1%p]){
                ll x=i*b-mp[p1*z1%p];
                if(x&1){
                    cout<<x<<'\n';
                    goto kkk;
                }
            }
            if(mp[p1*z2%p]){
                ll x=i*b-mp[p1*z2%p];
                if(!(x&1)){
                    cout<<x<<'\n';
                    goto kkk;
                }
            }
        }
        cout<<-1<<'\n';
        kkk:continue;
    }
}

1008 Maximal submatrix

思路是将矩阵的信息进行转换,转换成每个点的数字表示继续往下不递减的序列的长度
1 2 1 4 ------ 4 2 3 1
2 3 2 3 ------ 3 1 2 1
3 2 3 2 ------ 2 2 1 1
4 2 1 1 ------ 1 1 1 1
转化完成后,就是计算每一行的所有区间的最小值乘以区间的长度的值,取最大就是答案,暴力复杂度O(n3),我们可以用单调栈来计算这个数是最小的时候的左边界和右边界,复杂度将为O(n2)

#include<bits/stdc++.h>
#define FORz(i,t,n) for(int i=t;i<=n;i++)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;

int t,n,m,a[2005][2005],c[2005],b[2005][2005],l[2005],r[2005],zhan[2005],top;

int main()
{
    IOS;
    cin>>t;
    while(t--){
        cin>>n>>m;
        FORz(i,1,n) FORz(j,1,m) cin>>a[i][j];
        FORz(j,1,m) {
            int l=1,i;
            ++c[j];
            for(i=1;i<=n;i++){
                if(a[i][j]<a[i-1][j]){
                    ++c[j];
                    b[i][j]=c[j];
                }
                else b[i][j]=c[j];
            }
        }
        for(int j=1;j<=m;j++){
            int cnt=1;
            a[n][j]=1;
            for(int i=n-1;i>=1;i--){
                if(b[i][j]!=b[i+1][j]) cnt=0;
                ++cnt;
                a[i][j]=cnt;
            }
        }
        ll ans=m;
        //计算每一行的区间最小值乘以区间长度的最大值
        for(int p=1;p<=n;p++){
            for(int i=1;i<=m;++i) c[i]=a[p][i];
            for(int i=1;i<=m;++i)l[i]=0,r[i]=m+1;
            top=0;
            for(int i=1;i<=m;++i)
            {
                while(top&&c[zhan[top]]>=c[i])r[zhan[top--]]=i;
                l[i]=zhan[top];
                zhan[++top]=i;
            }
            for(int i=1;i<=m;++i) ans=max(ans,(ll)(r[i]-l[i]-1)*c[i]);
        }
        cout<<ans<<'\n';
    }
}

1009 KD-Graph

思路是先将边长排序,当边长相同的时候进行连边,连在一起的分为1组,各组之间没连边的边都严格大于D,边不同的时候判断组数是否为k,为k则成功。

#include<bits/stdc++.h>
#define FORz(i,t,n) for(int i=t;i<=n;i++)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;

int a[N],n,m,k,t;
struct ty{
    int u,v,c;
    bool operator <(ty kk){
        return c<kk.c;
    }
}e[N*5];
int find(int x){return a[x]==x?x:a[x]=find(a[x]);}
void merge(int x,int y){a[find(y)]=a[find(x)];}

int main()
{
    IOS;
    cin>>t;
    while(t--){
        cin>>n>>m>>k;
        int v,u,c;
        FORz(i,1,m) cin>>e[i].u>>e[i].v>>e[i].c;
        sort(e+1,e+m+1);
        FORz(i,1,n) a[i]=i;
        int b=n,ans=0; //b是组数,刚开始n表示每个点都在各自的组
        FORz(i,1,m){
            if(e[i].c!=e[i-1].c) {if(b==k) goto kkk;} //路径不一样时组数已经为k,这时已经满足所有组之间的路径大于ans
            if(find(e[i].u)==find(e[i].v)) continue;//两个点已经相连说明两个点之间已经有更小的路径,不用更新ans
            b--;merge(e[i].u,e[i].v);ans=e[i].c;//合并的点放入同一组,这时组中的路径最大值ans要更新
        }
        b==k?cout<<ans<<'\n':cout<<-1<<'\n';
        continue;
        kkk:cout<<ans<<'\n';
    }
}

1010 zoto

思路是用 num维护x轴上l到r上的y坐标,对sum进行分块,统计每块内num>0的个数

#include<bits/stdc++.h>
#define rep(i,x,y) for(auto i=(x);i<=(y);++i)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef long long ll;
using namespace std;
const int N=1e5+5;

int t,n,m,a[N],num[N],sum[N],ans[N],k=315;
struct ty{
    int l,r,L,R,id;
    //按组排序,如果按l的大小排序会超时,因为r的大小会使得while循环多次运行
    bool operator <(ty kk){
        if(l/k==kk.l/k) return r<kk.r;
        return l/k<kk.l/k;
    }
}b[N];

add(int x){
    if(++num[x]==1) sum[x/k]++;
}
dec(int x){
    if(--num[x]==0) sum[x/k]--;
}
int calc(int x,int y){
    int cnt=0;
    for(int i=x;i<min((x/k+1)*k,y+1);i++) cnt+=(num[i]>0);
    for(int i=x/k+1;i<y/k;i++) cnt+=sum[i];
    for(int i=max((x/k+1)*k,(y/k)*k);i<=y;i++) cnt+=(num[i]>0);
    return cnt;
}
int main()
{
    IOS;
    cin>>t;
    while(t--){
        memset(num,0,sizeof num),memset(sum,0,sizeof sum);
        cin>>n>>m;
        rep(i,1,n) cin>>a[i];
        rep(i,1,m) cin>>b[i].l>>b[i].L>>b[i].r>>b[i].R,b[i].id=i;
        sort(b+1,b+m+1);

        int pl=b[1].l,pr=b[1].r;
        rep(i,pl,pr) add(a[i]);
        ans[b[1].id]=calc(b[1].L,b[1].R);
        rep(i,2,m){
            while(b[i].l>pl) dec(a[pl]),pl++;
            while(b[i].l<pl) pl--,add(a[pl]);
            while(b[i].r>pr) pr++,add(a[pr]);
            while(b[i].r<pr) dec(a[pr]),pr--;
            ans[b[i].id]=calc(b[i].L,b[i].R);
        }
        rep(i,1,m) cout<<ans[i]<<"\n";
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值