原题:
C. Painting the Fence
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You have a long fence which consists of n sections. Unfortunately, it is not painted, so you decided to hire q painters to paint it. i-th painter will paint all sections x such that li≤x≤ri.
Unfortunately, you are on a tight budget, so you may hire only q−2 painters. Obviously, only painters you hire will do their work.
You want to maximize the number of painted sections if you choose q−2 painters optimally. A section is considered painted if at least one painter paints it.
Input
The first line contains two integers n and q (3≤n,q≤5000) — the number of sections and the number of painters availible for hire, respectively.
Then q lines follow, each describing one of the painters: i-th line contains two integers li and ri (1≤li≤ri≤n).
Output
Print one integer — maximum number of painted sections if you hire q−2 painters.
Examples
input
7 5
1 4
4 5
5 6
6 7
3 5
output
7
input
4 3
1 1
2 2
3 4
output
2
input
4 4
1 1
2 2
2 3
3 4
output
3
中文:
有一个篱笆,长度n为,现在有q个油漆工要给篱笆涂漆,每个油漆工能涂漆的区间为 [ l i , r i ] [l_i,r_i] [li,ri],现在让你选出q-2个油漆工,使得能把篱笆涂上最多的油漆。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=5005;
int one[maxn],two[maxn],q,n;
int mark[maxn];
pii seg[maxn];
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>q)
{
int a,b,tot=0;
memset(one,0,sizeof(one));
memset(two,0,sizeof(two));
memset(mark,0,sizeof(mark));
for(int i=1;i<=q;i++)
{
cin>>a>>b;
seg[i]=pii(a,b);
for(int j=a;j<=b;j++)
mark[j]++;
}
for(int i=1;i<maxn;i++)
{
if(mark[i]==1)
one[i]=1;
if(mark[i]==2)
two[i]=1;
if(mark[i]>0)
tot++;
}
for(int i=1;i<maxn;i++)
{
one[i]=one[i]+one[i-1];
two[i]=two[i]+two[i-1];
}
int ans=0,tmp1,tmp2,tmp3,tmp4;
for(int i=1;i<=q;i++)
{
for(int j=i+1;j<=q;j++)
{
if(seg[i].second<seg[j].first||seg[j].second<seg[i].first)
{
tmp1=one[seg[i].second]-one[seg[i].first-1];
tmp2=one[seg[j].second]-one[seg[j].first-1];
ans=max(ans,tot-tmp1-tmp2);
continue;
}
if(seg[i].second<=seg[j].second)
{
if(seg[i].first>=seg[j].first)
{
tmp1=one[seg[i].first-1]-one[seg[j].first-1];
tmp2=two[seg[i].second]-two[seg[i].first-1];
tmp3=one[seg[j].second]-one[seg[i].second-1];
ans=max(ans,tot-tmp1-tmp2-tmp3);
}
else
{
tmp1=one[seg[j].first-1]-one[seg[i].first-1];
tmp2=two[seg[i].second]-two[seg[j].first-1];
tmp3=one[seg[j].second]-one[seg[i].second-1];
ans=max(ans,tot-tmp1-tmp2-tmp3);
}
continue;
}
if(seg[j].second<=seg[i].second)
{
if(seg[j].first>=seg[i].first)
{
tmp1=one[seg[j].first-1]-one[seg[i].first-1];
tmp2=two[seg[j].second]-two[seg[j].first-1];
tmp3=one[seg[i].second]-one[seg[j].second-1];
ans=max(ans,tot-tmp1-tmp2-tmp3);
}
else
{
tmp1=one[seg[i].first-1]-one[seg[j].first-1];
tmp2=two[seg[j].second]-two[seg[i].first-1];
tmp3=one[seg[i].second]-one[seg[j].second-1];
ans=max(ans,tot-tmp1-tmp2-tmp3);
}
}
}
}
cout<<ans<<endl;
}
return 0;
}
解答:
油漆工只有5000个,o(n^2)的算法是可以解决的。那么可以先将所有油漆工在礼拜上都涂上对应区间的油漆,枚举两个油漆工,去掉这两人涂油漆的区间,得到去掉被枚举两个油漆工之后得到被涂的篱笆数,需要O(1)的处理。
这里需要用前缀累加和的方式来判断,设置数组one[i]和two[i]记录前每个篱笆被涂了一次和涂了两次的前i项和,那么在去掉枚举的两个油漆工的涂油漆区间时,两个油漆工相交的部分使用two数组相减相交端点得到,未相交的区间使用one得到即可。