CS R46 C(思维套路),D(好题,数据结构维护) E(???)

题意:长度为n的序列a,其每个元素减去一个正数X得到序列b
n<=1e3,a[i]<=1e9.给出序列A(序列a,b打乱顺序).求出序列A中是否有满足的序列a和正数X存在.若存在输出解.


mn肯定为序列b元素,并且有个元素在序列a中和它对应(枚举该元素即差值)
则接下来未被标记中最小的肯定也为序列b元素,(若为序列a元素,则要存在一个比它小的能和它配对)

根据差值 用map记录其对应元素.

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii;
typedef long long ll;
const int N=2e3+20,inf=0x3f3f3f3f;
int n,a[N],vis[N];
map<int,int> mp;
vector<int> ans;
int main()
{
	cin>>n;
	n*=2;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);//mp[a[i]]++;
	sort(a+1,a+1+n);
	int m=n/2;
	for(int i=2;i<=n;i++)
	{
		int dif=a[i]-a[1],num=0;	
		mp.clear(),ans.clear();
		for(int k=1;k<=n;k++)
		{
			if(mp[a[k]])
			{
				mp[a[k]]--;
				ans.push_back(a[k]);
				num++;
			}
			else
				mp[a[k]+dif]++;	
		}
		if(num==m)
		{
			printf("%d\n",dif);
			for(int k=0;k<ans.size();k++)
				printf("%d ",ans[k]);
			printf("\n");
			return 0;
		}
	}
	puts("-1");
	return 0;
}

题意:n*m矩阵,只包含01.求出有多少个正方形子矩阵其边界都为1并且主对角线都为1.n,m<=2000.

核心:枚举全1主对角线贡献,即算出有多少个合法子矩形的主对角线在该条线上
预处理出每个1的二元组(r[i],l[i]) r[i]:向下和向右最多同时扩展长度.l[i]类似.
若对角线上(i,j)分别为左上和右下 则要满足i+r[i]-1>=j && i>=j-l[i]+1 && i<=j 
处理对角线时,每处理一个元素 将其i+r[i]-1压入优先队列.
当枚举当前位置j为右下角时,用j淘汰掉队列若干个元素后,队列中剩下元素满足成为左上角的第一个条件

用BIT记录队列中的元素i,则除了BIT前j-l[i]个不满足第二个条件外,其余都满足.复杂度O(n^2logn).

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii;
typedef long long ll;
const int N=2e3+20,inf=0x3f3f3f3f;
char s[N][N];
int rig[N][N],lef[N][N],dow[N][N],up[N][N];
int row,col;
priority_queue<ii> q;
ll ans;
int c[N];
int lowbit(int x){return x&-x;}
void add(int x,int val)
{
	int n=max(row,col);
	for(int i=x;i<=n;i+=lowbit(i))
		c[i]+=val;
}
int query(int x)
{
	int res=0;
	for(int i=x;i>=1;i-=lowbit(i))
		res+=c[i];	
	return res;
}
void po()
{
	int w=q.top().second;
	add(w,-1);
	q.pop();
}
void pus(int x,int y)
{
	q.push(ii(-x,y));
	add(y,1);
}
void solve(int i,int j)
{
	for(int x=i,y=j;x<=row&&y<=col;x++,y++)
	{
		if(s[x][y]=='1')
		{
			while(!q.empty()&& -q.top().first<x)
				po();
			pus(x+min(rig[x][y],dow[x][y])-1,x);
			ans+=q.size()-query(x-min(lef[x][y],up[x][y]));	
		}
		else
			while(!q.empty())
				po();
	}
	while(!q.empty())
		po();
}
int main()
{
	cin>>row>>col;
	for(int i=1;i<=row;i++)
		scanf("%s",s[i]+1);
	for(int i=1;i<=row;i++)
		for(int j=1;j<=col;j++)
			if(s[i][j]=='1')
				lef[i][j]=lef[i][j-1]+1,up[i][j]=up[i-1][j]+1;
	for(int i=row;i>=1;i--)
		for(int j=col;j>=1;j--)
			if(s[i][j]=='1')
				rig[i][j]=rig[i][j+1]+1,dow[i][j]=dow[i+1][j]+1;
	ans=0;
	for(int i=1;i<=row;i++)
		for(int j=1;j<=col;j++)
			if(i==1||j==1)
				solve(i,j);
			
	cout<<ans<<endl;
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值