TOJ8514:连续自然数和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll m;
cin>>m;
for(ll a1=sqrt(m*2);a1>=2;a1--)
{
ll a2=2*m/a1;
ll l,r;
l=(a2-a1+1)/2;
r=(a1+a2-1)/2;
if(2*m==a1*a2&&a2>a1&&(a1+a2)%2==1)
cout<<l<<" "<<r<<endl;
}
}
思路:如图推理可得,a1,a2只能为一奇数一偶数,设sum由a1和a2相乘所得,通过枚举即可求出所有可能得L,R区间。
TOJ8515: 逛画展
#include<bits/stdc++.h>
using namespace std;
int vis[2100],num[1100000];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n,m,cnt=0,Min=999999,now=0,st=0,end=0;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>num[i];
int left=1;
int right=0;
while(right<=n)
{
if(cnt<m)
{
now++;
right++;
if(vis[num[right]]==0)
cnt++;
vis[num[right]]++;
}
else if(cnt==m)
{
now--;
vis[num[left]]--;
if(vis[num[left]]==0)
cnt--;
left++;
}
if(cnt==m&&now<Min)
{
Min=now;
st=left;
end=right;
}
}
cout<<st<<" "<<end<<endl;
}
思路:1.左右指针分别用于指向左右区间端点;
2.将右指针不断向右移动,直到到达要求的包含所有画师的作品为止;
3.将左指针不断向右移动,直到不再达到包含所有画师的作品的要求为止;
4.若右指针仍然在数组范围以内,重复第二三步;
TOJ7923: 差为给定数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll str[110000];
int main()
{
ll n,aim,ans=0;
cin>>n>>aim;
for(ll i=1;i<=n;i++)
cin>>str[i];
ll right1=1;
ll right2=1;
ll now;
for(now=1;now<=n;now++)
{
while(right1<=n&&str[right1]-str[now]<aim)
{
right1++;
}
while(right2<=n&&str[right2]-str[now]<=aim)
{
right2++;
}
ans+=right2-right1;
}
cout<<ans<<endl;
}
思路:1.定义三个指针,左指针用于指向区间左端,右指针二用于指向区间右端,右指针一用于指向与区间右端相同数值的数的最左端;
2.用左指针从左往右遍历数组,每次循环更新右指针一和右指针二,直到两者指向小于和小于等于目标差值的数,所得右指针一二之差即为满足目标差值的区间,更新所需输出对数的值;
TOJ8519: 单词背诵
#include<bits/stdc++.h>
using namespace std;
map<string,int>vis;
map<string,int>sign;
map<string,int>need;
string s1;
string str[110000];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int m,n,cnt=0,now=0,Min=999999,st=0,end=0,aim=0;
cin>>n;
while(n--)
{
cin>>s1;
need[s1]=1;
}
cin>>m;
for(int M=1;M<=m;M++)
{
cin>>str[M];
if(need[str[M]]==1&&sign[str[M]]==0)
{
aim++;
sign[str[M]]=1;
}
}
int left=1,right=0;
while(right<=m)
{
if(cnt<aim)
{
now++;
right++;
if(need[str[right]]==1&&vis[str[right]]==0)
cnt++;
vis[str[right]]++;
}
else if(cnt==aim)
{
now--;
vis[str[left]]--;
if(need[str[left]]==1&&vis[str[left]]==0)
cnt--;
left++;
}
if(cnt==aim&&now<Min)
{
Min=now;
st=left;
end=right;
}
}
cout<<aim<<endl<<Min<<endl;
}
思路:与TOJ8515: 逛画展思路大致,
但是1:需要用String的数组来存储所有单词,用stl map来标记字符串的出现次数;
2:需要记录文章包含的最多的所需单词的数量,并以此求出满足包含这些单词的最短区间的长度;
TOJ 8520: Xor Sum 2
#include<bits/stdc++.h>
using namespace std;
int num[220000];
int sum=0,cnt=0,now=0;
int XOROperator(int &a,int &b){
return a^b;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>num[i];
int left=1;
int right=0;
for(left=1;left<=n;left++)
{
while((XOROperator(sum,num[right+1]))==sum+num[right+1]&&right<n)
{
sum+=num[right+1];
right++;
now++;
cnt++;
}
now--;
cnt+=now;
sum-=num[left];
}
cout<<cnt<<endl;
}
分析:假设有三个数,由图可知,当5和10和16异或等于两者之和时,10和16异或也等于两者之和。为什么呢?因为当5和10和16异或等于三者之和时,说明5和10的二进制编码的1和0全部不对应,而16和(5和10)的二进制编码全部不对应,则说明10和16的二进制编码也全部不对应,由此可得结论:当序号为1到X的区间异或结果等于全部之和时,序号2到X,3到X...也是区间异或结果等于全部之和。
思路:由此结论,可以定义左右指针,遍历左指针,并在遍历时将右指针向右移动到满足条件的最右端为止,每次循环将区间长度加入结果,即得出所有可能得区间。