C
题意:给你长度为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
题意:给定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
题意:给你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;
}