尺取法二分河南省第十三届ICPC大学生程序设计竞赛 C题
Posted Tanphoon
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尺取法二分河南省第十三届ICPC大学生程序设计竞赛 C题相关的知识,希望对你有一定的参考价值。
题目链接:https://ac.nowcoder.com/acm/contest/57784/C
来源:牛客网
题目描述
有一个长度为 \\(n\\) 的序列 \\(a_i\\) 和常数 \\(K\\)。
总共选 \\(m\\) 次,每次选一个连续区间 \\([L_i,R_i]\\) ,问这个区间中存在多少个连续子区间满足,区间中不同的数的个数不小于 \\(K\\)。
首先用尺取法统计区间数字出现的次数,这是一种简单易行的暴力法。
定义 mp[x]
表示数字 \\(x\\) 出现的次数;定义 cnt
表示区间内不同数的个数;定义 f[i]
表示区间 \\([i, f_i]\\) 中有 \\(k\\) 个不同的数。
用指针 \\(l, r\\) 单向扫描,从数列头扫到尾,\\(l, r\\) 最终落在区间最右边。
\\(r\\) 每向右扫描一个数字,它出现的次数加1;\\(l\\) 每向右扫描一个数字,它出现的次数减1。
当 cnt
的值等于 \\(K\\) 时,记录此时的右指针 \\(f[l]\\gets r\\)。
for (int l = 1, r = 1; l <= n; l++)
while (cnt < k && r <= n)
if (++mp[a[r++]] == 1)
cnt++;
if (cnt == k)
f[l] = r - 1;
else
f[l] = n + 1;
if (--mp[a[l]] == 0)
cnt--;
我们考虑如何计算子区间个数。
for (int i = l; i <= r; i++)
if (f[i] <= r)
cnt += r - f[i] + 1;
考虑到 \\(f[i]\\) 是单调递增的,那么可以二分查找 p = upper_bound(r) - 1
答案为 \\((r+1)(p-l+1)-\\sum\\limits_i=l^i\\le p f[i]=(r+1)(p-l+1)-(s[p]-s[l-1])\\)。\\(s[i]\\) 为 \\(f[i]\\) 的前缀和。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5;
int a[N], f[N];
ll s[N];
int n, m, k, cnt;
map<int, int> mp;
void add(int x)
if (++mp[a[x]] == 1)
cnt++;
void del(int x)
if (--mp[a[x]] == 0)
cnt--;
int main()
cin >> n >> m >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
// 尺取法,f[i] 表示区间 [i, f[i]] 中有k个不同的数
for (int l = 1, r = 1; l <= n; l++)
while (cnt < k && r <= n)
add(r++);
if (cnt == k)
f[l] = r - 1;
else
f[l] = n + 1;
del(l);
// 计算f的前缀和
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + f[i];
ll ans = 0;
while (m--)
ll l1, r1;
cin >> l1 >> r1;
int l = min(l1 ^ ans, r1 ^ ans) + 1;
int r = max(l1 ^ ans, r1 ^ ans) + 1;
// 二分
ll p = upper_bound(f + 1, f + 1 + n, r) - f - 1;
ans = 0;
if (p >= l)
ans = (r + 1) * (p - l + 1) - (s[p] - s[l - 1]);
cout << ans << endl;
河南省第十三届ICPC大学生程序设计竞赛重现赛感悟
1年前就补过了也写过一篇题解。
时隔1年又刷了一遍看看1年的时间自己成长了多少。
本篇只讲自己的感悟。
A签到过了,F也是签到但是犯了一个sb的错误就是坐标我跟字符数组一块定义成了char。导致一直wa,调了好久。M是思维题也是签到。
L我当时记得特别清楚就是撒点暴力,调了两次的精度过了。J其实当时补的时候没补,想看看1年后自己能否独立想出来。结果还是不会,后来找了篇题解认真的看了看,挺秒的但是感觉还是不太清楚。于是上床睡觉,思考。思考了一会儿懂了,写代码过了。I是dfs暴力搜一会儿就过了。感觉1年的时间自己确实成长了,但是感觉还是太弱。
//A
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int a[N][N],n,m,h,w;
bool check(int x,int y)
int b[10]=0,c[10]=10;
b[0]=x,c[0]=y;
b[1]=x,c[1]=y+w-1;
b[2]=x+h-1,c[2]=y;
b[3]=x+h-1,c[3]=y+w-1;
set<int>st;
for(int i=0;i<4;i++)
x=b[i],y=c[i];
if(x<=0||x>n|y<=0||y>m) return false;
st.insert(a[x][y]);
if(st.size()!=1) return false;
return true;
int main(void)
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) cin>>a[i][j];
cin>>h>>w;
bool flag=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(check(i,j)) flag=1;
if(flag) puts("YES");
else puts("NO");
return 0;
//F
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int n,m;
char a[N][N];
int xx,yy,x,y,dx[N],dy[N];
int main(void)
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
if(a[i][j]=='#') x=i,y=j;
if(a[i][j]=='*') dx[i]++,dy[j]++;
for(int i=1;i<=n;i++) if(dx[i]==n) xx=i;
for(int i=1;i<=m;i++) if(dy[i]==m) yy=i;
int flag1=1;
if(y<yy) flag1=-1;
int flag2=1;
if(x>xx) flag2=-1;
cout<<flag1*abs(y-yy)<<" "<<flag2*abs(x-xx)<<endl;
return 0;
//I
#include<bits/stdc++.h>
using namespace std;
int t,flag,st[105],mp[105];
vector<int>ve;
void dfs(int index)
if(index==(ve.size()-1))
flag=1;
return;
for(int i=0;i<ve.size();i++)
int j=ve[i];
if(!st[j])
if(!st[(j+2)%8+1])
st[(j+2)%8+1]=1;
dfs(index+1);
st[(j+2)%8+1]=0;
if(!st[(j+4)%8+1])
st[(j+4)%8+1]=1;
dfs(index+1);
st[(j+4)%8+1]=0;
int main(void)
cin>>t;
while(t--)
string s; cin>>s;
s="0"+s;
ve.clear();
flag=0;
for(int i=1;i<=8;i++) st[i]=0,mp[i]=0;
for(int i=1;i<=8;i++)
if(s[i]=='0') ve.push_back(i);
else st[i]=1;
dfs(0);
if(flag) puts("Yes");
else puts("No");
return 0;
对顶搞,红色的作为一个分割线指针,来回移动即可。
//J
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long int LL;
int n,n1,n2,a[N],tr[N];
struct nodeint x,id;S[N];
bool cmp(node a,node b)return a.x>b.x;
int lowbit(int x)return x&(-x);
void add(int x,int v)
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=v;
int query(int x)
int sum=0;
for(int i=x;i;i-=lowbit(i)) sum+=tr[i];
return sum;
int main(void)
cin>>n1>>n2;
for(int i=n1;i>=1;i--) cin>>a[i];
for(int i=n1+1;i<=(n1+n2);i++) cin>>a[i];
n=n1+n2;
for(int i=1;i<=n;i++) S[i]=a[i],i,add(i,1);
sort(S+1,S+n+1,cmp);
LL ans=0;
for(int i=1;i<=n;i++)
int id=S[i].id;
if(id>n1) //第二堆
ans+=query(id)-query(n1)-1;
else//第一堆
ans+=query(n1)-query(id);
add(id,-1),n1=id;
cout<<ans<<endl;
return 0;
//L
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int main(void)
int t; cin>>t;
while(t--)
double a,b,c,d; cin>>a>>b>>c>>d;
int cnt1=0,cnt2=0;
for(double i=-8;i<=8;i+=0.001)
for(double j=-8;j<=8;j+=0.0025)
double sum1=(i*i)/(a*a)+(j*j)/(b*b);
double sum2=(i*i)/(c*c)+(j*j)/(d*d);
if(sum1<=1||sum2<=1) cnt1++;
cnt2++;
double ans=16.0*16*cnt1/cnt2;
printf("%.1lf\\n",ans);
return 0;
//M
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,k,a[N];
int main(void)
cin>>n>>m>>k;
long long int sum=0;
while(m--)
int l,r; cin>>l>>r;
sum+=(r-l);
for(int i=1;i<=k;i++) cin>>a[i];
cout<<sum<<endl;
return 0;
以上是关于尺取法二分河南省第十三届ICPC大学生程序设计竞赛 C题的主要内容,如果未能解决你的问题,请参考以下文章
河南省第十三届ICPC大学生程序设计竞赛 G.Elo mountains