D.Non-Puzzle:Error Permulation
利用双指针和差分
假设现在的区间是以x作为左端点,然后如果[x,y]这段区间是不符合条件的,那么就标记一下y这个位置,当我们枚举以x为左端点的区间时,[x,y]这段区间就不符合,我们就需要搞一个数组,记录当以i为左端点时,以y为右端点不满足条件,可以在y这个位置加一个1来标记一下,但是以x为左端点时,可能连续的一段区间作为右端点都是不满足条件的,那么我们就需要将整个区间的每个数都加一个1来标记一下,此时可以利用差分数组来操作,然后最后求一遍前缀和就可以得到以x为左端点,y是否标记为1了(有可能以x为左端点,然后y被标记了好几次,即加了好多次1,但是没关系,只要y被标记过,就不是0了,不是0就说明[x,y]这段区间不满足条件)
最后我们只要从左到右枚举每一个数,枚举以它为左端点的所有区间(这样可以做到不重不漏地枚举所有区间),实际上我们是在枚举右端点,只要右端点没有被标记过,那么该区间符合条件,就计数+1
具体标记的方法:
对于每一个数,首先我们枚举包含它的所有不满足条件的区间,即将该数作为区间的第一个位置它又是第一小的,将该数作为区间的第二个位置它又是第二小的...(具体看注释)
然后如果区间[x,y]不满足条件,那么以x为左端点,就标记一下y
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#include<vector>
#include<cstdio>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N=5010;
int a[N];
int d[N][N];
int n;
void solve() {
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=0;i<=n+1;i++){
for(int j=0;j<=n+1;j++){
d[i][j]=0;
}
}
for(int i=1;i<=n;i++){
int l=i,r=i;
while(r<n&&a[r+1]>a[i]) r++;//首先是a[i]作为区间的第一个数同时又是第一小的
d[i][l]++,d[i][r+1]--;//以i为左端点,[l,r]这整段区间都标记为1,表示以i为左端点,[i,l],[i,l+1],[i,l+2],...[i,r]都不符合条件
//接下来开始找a[i]作为区间的第二个数同时又是第二小的,a[i]作为区间的第三个数同时又是第三小的...
for(int j=i-1;j>=1;j--){
//如果a[j]本来就小于a[i],那么以j为左端点,[l,r]这整段区间也都标记为1,表示以j为左端点,[j,l],[j,l+1],[j,l+2],...[j,r]都不符合条件
//因为这些区间a[i]是第i-j+1个数同时是第i-j+1小的
//如果a[j]大于a[i],那么就要从r+1开始找哪些数作为右端点是不符合的
if(a[j]>a[i]){
l=r+1;
r=l;
while(r<n&&a[r+1]>a[i]) r++;
}
if(l<=n) d[j][l]++,d[j][r+1]--;
}
}
//最后统计答案
int ans=0;
for(int i=1;i<=n;i++){
int f=0;
for(int j=i;j<=n;j++){
f+=d[i][j];
if(f==0) ans++;
}
}
cout<<ans<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--)
solve();
return 0;
}
就一直贪心最大的正方形,在每次剩下的矩形中,取最大的正方形,这样的话肯定有解,不会出现NO的情况,直到左端点的坐标(x,y),x等于n或者y等于m,此时已经将整个矩形全部取完了
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
#include<deque>
#define endl '\n'
//#define int long long
using namespace std;
typedef long long ll;
int n,m;
struct node {
int x,y,l;
};
void solve() {
cin>>n>>m;
vector<node>ans;
int x=0,y=0;
while(x<n&&y<m) {
int a=n-x,b=m-y;
if(a<=b) {
ans.push_back({x,y,a});
y+=a;
} else {
ans.push_back({x,y,b});
x+=b;
}
}
cout<<"YES"<<endl;
cout<<ans.size()<<endl;
for(auto v:ans) cout<<v.x<<" "<<v.y<<" "<<v.l<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--)
solve();
return 0;
}
从贡献度的角度考虑,从1到5e5,枚举每一个点,对于每一个点,我们考虑包含它的所有区间,当然产生答案的区间只由当前点i作为左端点的区间产生,该区间肯定是新枚举到的,所以由它对答案产生贡献的话,不会出现重复的情况
如果枚举到当前区间刚好是n个区间对,那么就可以统计答案,即除去当前枚举到的区间,算之前两条线段都覆盖该点的区间对数量sum,2^sum即为枚举到的当前区间对答案的贡献
AC代码;
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#include<vector>
#include<cstdio>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N=5e5+10,mod=1e9+7;
vector<int>l[N],r[N];//l[x]放置以x为左端点的区间对下标,r[x]放置以x为右端点的区间对下标
int vis[N];//vis[x]表示第x对区间中有几条线段(0,1,2)覆盖了当前位置
int n;
//快速幂
int qmi(int a,int k){
int res=1;
while(k){
if(k&1) res=(ll)res*a%mod;
a=(ll)a*a%mod;
k>>=1;
}
return res;
}
void solve() {
cin>>n;
for(int i=1;i<=n;i++){
int l1,r1,l2,r2;
cin>>l1>>r1>>l2>>r2;
l[l1].push_back(i),r[r1].push_back(i);
l[l2].push_back(i),r[r2].push_back(i);
}
int res=0;
int num=0;//覆盖了点i的区间有几个
int cnt=0;//当前使用了几对区间对
//从贡献度的角度考虑
//从1枚举到5e5,对于每一个点i,我们都枚举包含它的所有区间,具体操作是删去前面枚举过的不包含它的区间(即删去右端点为i-1的区间),再加上左端点为i的区间
//我们统计覆盖某个点的n对区间的选择方式数量,然后全部加起来,注意中间要去重
for(int i=1;i<=5e5;i++){
//删去右端点为i-1的区间
for(auto j:r[i-1]){
num--;//覆盖点i的区间个数-1
vis[j]--;//第j对区间中覆盖当前位置i的线段条数-1
if(!vis[j]) cnt--;//如果第j对区间中覆盖当前位置i的线段条数为0,那么说明当前使用的区间对数量也相应地-1
}
//加上左端点为i的区间
for(auto j:l[i]){
if(!vis[j]) cnt++;//如果第j对区间是第一次覆盖当前位置,那么当前使用的区间对数量+1
num++;//覆盖点i的区间个数+1
vis[j]++;//第j对区间中覆盖当前位置i的线段条数+1
//如果当前使用的区间对数量刚好等于n,说明点i被n对区间覆盖了,那么就可以统计答案了
//第j对区间作为枚举的n对区间的最后一对区间,我们需要算在它之前有几对区间是两条线段均覆盖到点i的
//然后根据乘法原理,有几对区间是两条线段均覆盖到点i的,就乘几个2(由于那些只有一条线段覆盖到点i的是必选的,所以是乘1,乘1可忽略)
if(cnt==n){
//假设有x对区间只有一条线段覆盖点i,有y对区间两条线段均覆盖点i
//那么num=x+2*y,cnt=x+y,所以y=num-cnt
//由于我们要算的是之前有几对区间是两条线段均覆盖到点i的,所以如果vis[j]为1,那么num-cnt即为在这之前两条线段均覆盖到点i的区间对数量
//如果vis[j]为2,那么我们算的两条线段均覆盖到点i的区间对数量多加了1,所以要减1,故统一为加一个1-vis[j]
int sum=num-cnt+1-vis[j];
res=(res+qmi(2,sum))%mod;
}
}
}
cout<<res<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
// cin>>t;
while(t--)
solve();
return 0;
}