Codeforces Round #841 (Div. 2) and Divide by Zero 2022

C

Even Subarrays

题意:给你长度为n的数组a,问(i,j)对数满足ai^...^aj有偶数个因子。

思路:注意到一个数x=p1^a1 * p2^a2 * ...,则因子个数为(a1+1)*(a2+1)*....,只有所有ai为偶数时(此时x是完全平方数),因子个数为奇数。我们用总的对数减去奇数的对数即为答案。我们可以预处理前缀异或和,枚举平方数来统计数量。

对于每个前缀异或和sum[i],枚举平方数,若满足sum[i]^(j*j)==sum[k],就说明sum[i]^sum[k]==j*j是非法答案。那么可以通过这个方法去遍历每一个以i结尾的不满足条件的答案。

#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ios cin.sync_with_stdio(false)
#define PII pair<int,int>
#define int long long
typedef long long ll;
const int N=1e6+10,M=3e5+10;
const int inf=0x3f3f3f3f;

using namespace std;
int n;
int p[N];
int a[N],sum[N];
int cnt[N];
int t;
void init()
{
    for(int i=1;i*i<=M;i++)
        p[++t]=i*i;
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]^a[i];
    }
    cnt[0]=1;
    ll ans=(n+1)*n/2;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=t;j++)
        {
            ans-=cnt[sum[i]^p[j]];
        }
        cnt[sum[i]]++;
    }
    for(int i=1;i<=n;i++) cnt[sum[i]]=0;
    cout<<ans<<'\n';
}
signed main()
{
    //ios;
    init();
    int _t=1;
    cin>>_t;
    while(_t--) solve();
    system("pause");
    return 0;
}

D

Valiant's New Map

题意:给定n*m的矩阵,a[i,j]表示建筑的高度,问最大的r,满足r*r矩阵内的建筑高度都大于等于r。

思路:二分边长r,在check函数中,若a大于等于r,则记为r;否则记为0。利用二维前缀和检查。

因为n*m<1e6,可以将二维映射成一维。时间复杂度O(n*m log(min(n,m)) )

#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ios cin.sync_with_stdio(false)
#define PII pair<int,int>
#define int long long
typedef long long ll;
const int N=1e6+10;
const int inf=0x3f3f3f3f;

using namespace std;
int a[N];
int sum[N];
int n,m;
int find(int x,int y)
{
    return (x-1)*m+y;
}
bool check(int x)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(a[find(i,j)]>=x) sum[find(i,j)]=x;
            else sum[find(i,j)]=0;
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            sum[find(i,j)]=sum[find(i-1,j)]+sum[find(i,j-1)]-sum[find(i-1,j-1)]+sum[find(i,j)];
    
    for(int i=x;i<=n;i++)
        for(int j=x;j<=m;j++)
            if(sum[find(i,j)]-sum[find(i-x,j)]-sum[find(i,j-x)]+sum[find(i-x,j-x)]==(ll)x*x*x) return 1;
    return 0;
}
void solve()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[find(i,j)];

    int l=1,r=min(n,m);
    while(l<r)
    {
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l<<'\n';
}
signed main()
{
    //ios;
    int _t=1;
    cin>>_t;
    while(_t--) solve();
    system("pause");
    return 0;
}

E

Graph Cost

题意:给你n点,编号1~n,规定只能在u号点和v号点之间加一条边权为gcd(u,v)的边。每次操作,可以加入k条权重为k+1的边,花费为k+1。问能否加入m条边,若能输出最小花费;不能输出-1

思路:先处理 gcd(u,v)=i的点对的个数,记为f[i].因为1~n中两个数的最大公约数为n/2,所以当i>n/2时,f[i]=0;然后对于因子k,1~n中包含因子k的数的个数共n/k个,从中任取两数C(n/k,2),那么最大公约数都是k的倍数,即1*k,2*k,3*k...

为使总花费最小,花费从n到2贪心,若不能凑出m输出-1.

#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
#define ios cin.sync_with_stdio(false)
#define PII pair<int,int>
#define int long long
typedef long long ll;
const int N=1e6+10;
const int inf=0x3f3f3f3f;

using namespace std;
int f[N];//gcd(u,v)=i的点对的个数
int n,m;
void solve()
{
    cin>>n>>m;
    for(int i=n/2+1;i<=n;i++) f[i]=0;
    for(int i=n/2;i>=1;i--)
    {
        f[i]=n/i*(n/i-1)/2;
        for(int j=2;i*j<=n;j++)
            f[i]-=f[i*j];
    }
    ll ans=0;
    while(m&&n>1)
    {
        if(m&&m>=n-1)
        {
            ll t=min(f[n]/(n-1),m/(n-1));
            ans+=t*n;
            m-=t*(n-1);
        }
        n--;
    }
    if(m==0) cout<<ans<<'\n';
    else cout<<-1<<'\n';
}
signed main()
{
    //ios;
    int _t=1;
    cin>>_t;
    while(_t--) solve();
    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_62615329/article/details/129249608