对一些给出限制条件的题,一眼可能数据量很大或者直接不好想,可以化式子找出限制条件或者规律。
遇到就会总结到这里,持续更新。
P2789 直线交点数
- 求交点数的种数
- n条中有p条平行,剩下(n-p)条与p条一定相交,交出来p*(n-p)个点,这是固定的
- 剩下的(n-p)再求种数—>递归
const int N=1e3+10;
int ans=0;
bool f[N];
void find(int n,int cnt){
if(n==0){
if(!f[cnt])ans++;//如果这一种之前没有
f[cnt]=1;
}else{
for(int p=n;p>=1;p--){//平行线个数枚举
find(n-p,p*(n-p)+cnt);//讨论n-p个的数目
}
}
}
void solve(){
int n;
cin>>n;
find(n,0);
cout<<ans;
}
2051D 二分+离散化
- 推式子 x ≤ s u m − a i − a j ≤ y x\leq sum-a_i-a_j\leq y x≤sum−ai−aj≤y
- 可得到 s u m − a i − y ≤ a j ≤ s u m − a i − x sum-a_i-y\leq a_j\leq sum-a_i-x sum−ai−y≤aj≤sum−ai−x,枚举 a i a_i ai可以找出对应 a j a_j aj,记录个数即可
- 找 a j a_j aj用二分法lower_bound(第一个大于等于目标的数)确定左右边界
- 记录个数用前缀和优化
const int N=2e5+10;
// int a[N],b[3*N];
vector<int>axis;
int find(int aim){
return lower_bound(axis.begin(),axis.end(),aim)-axis.begin();
}
void solve(){
int n,x,y,sum=0;
// memset(a,0,sizeof a);
// memset(b,0,sizeof b);
//使用memset会超时
axis.clear();
axis.push_back(-1e18);
cin>>n>>x>>y;
vector<int>a(n+1),b(3*n+1);
//用来记录轴上的点:a中每个点 和 每个a[i]对应a[j]的左右界
forr(i,1,n){
cin>>a[i];
sum+=a[i];
}
forr(i,1,n){
axis.push_back(a[i]);
axis.push_back(sum-x-a[i]);
axis.push_back(sum-y-a[i]);
}
//对数轴离散化
sort(axis.begin(),axis.end());//使用unique先排序
axis.erase(unique(axis.begin(),axis.end()),axis.end());//unique返回不重复数的末端
forr(i,1,n)b[find(a[i])]++;//记录a中每个数位置
forr(i,1,3*n){//求前缀和 3*n因为之前向axis中放入3*n个数
b[i]+=b[i-1];
}
int cnt=0,rest=0;
forr(i,1,n){//遍历记个数
cnt+=b[find(sum-x-a[i])]-b[find(sum-y-a[i])-1];//前缀和优化
if(sum-2*a[i]>=x&&sum-2*a[i]<=y){
rest++;//记录i=j个数
}
}
cout<<((cnt-rest)>>1)<<endl;
}
使用memset超时 对2e5的空间反复赋值/声明
2e5*1e4=2e9 计算机每秒1e8
必然会超时
所以用动态数组 用多少空间申请多少
1520D 化式子优化
思路
- 数据范围2e5 O(n2)做法超时
- 题中条件 i < j , a [ j ] − a [ i ] = j − i i<j,a[j]-a[i]=j-i i<j,a[j]−a[i]=j−i
- 等式移项 a [ j ] − j = a [ i ] − i a[j]-j=a[i]-i a[j]−j=a[i]−i
const int N=2e5+10;
int a[N];
void solve(){
int n;
cin>>n;map<int,int>m;
forr(i,1,n){
cin>>a[i];
m[a[i]-i]++;
}
int cnt=0;
for(auto i:m){
int t=i.second;
if(t>1)cnt+=(t*(t-1))/2;
}
cout<<cnt<<endl;
return;
}
1541B 化式子优化
题意
思路
- O(n2)暴力做法超时
forr(i,1,n){
forr(j,i+1,n){//这个可以优化 不必i+1~n全遍历
}
}
- 优化:题中 a [ j ] × a [ i ] = i + j a[j]\times a[i]=i+j a[j]×a[i]=i+j 已知 i i i后 j = a [ j ] × a [ i ] − i j=a[j]\times a[i]-i j=a[j]×a[i]−i
- a[j]枚举1,2,3,…直到 j = a [ j ] × a [ i ] − i > = n j=a[j]\times a[i]-i>=n j=a[j]×a[i]−i>=n
代码
void solve(){
int n;cin>>n;
vector<int>a(n+1);
forr(i,1,n)cin>>a[i];
int ans=0;
forr(i,1,n){
for(int j=a[i]-i;j<=n;j+=a[i]){
if(j<i+1)continue;
if(a[j]*a[i]==i+j)ans++;
}
}
cout<<ans<<endl;
}
2041B
- w+b个点摆 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)的三角形
- 不管黑白, n 2 + n − 2 ( w + b ) = 0 n^2+n-2(w+b)=0 n2+n−2(w+b)=0的根取floor是 n ′ n' n′最多的行数
- 考虑黑白,1~ n ′ n' n′少的颜色总能在根的条件下凑满行,多的颜色补齐其他行
void solve(){
int w,b;
cin>>w>>b;
double d=w+b;
double n=floor((sqrt(1+8*d)-1)/2.0);
cout<<n<<endl;
}
父子局
用x表示y
1
n
=
1
x
+
1
y
{1\over n}={1\over x}+{1\over y}
n1=x1+y1
y
=
n
x
x
−
n
y={{nx}\over {x-n}}
y=x−nnx
y应该是个整数,最简单的就是
x
=
n
+
1
x=n+1
x=n+1,那么
y
=
n
2
+
n
y=n^2+n
y=n2+n
再限制x,y不同
void solve(){
int x,y,n;
cin>>n;
x=n+1;
y=n*n+n;
if(x==y)cout<<-1<<' '<<-1<<endl;
else cout<<x<<' '<<y<<endl;
}