2020 Dec.
学习规划
字符串:
哈希
KMP
Trie树
字符串
P3370 【模板】字符串哈希
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
typedef unsigned long long ull;
const ull modnum = 91815541;
int n,ans;
string s;
ull base = 17,h[10005],mypow[10005];
ull H(string str)
{
ull num = 0;
for(int i = 0;i < s.size();i ++)
num = ((num*base)%modnum + ull(s[i]))%modnum;
return num;
}
int main()
{
scanf("%d",&n);
mypow[0] = 1;
for(int i = 1;i <= 1505;i ++)
mypow[i] = (mypow[i]*base)%modnum;
for(int i = 1;i <= n;i ++)
{
cin >> s;
h[i] = H(s);
}
sort(h+1,h+1+n);
for(int i = 1;i <= n;i ++)
if(h[i] != h[i+1])
ans ++;
printf("%d",ans);
return 0;
}
P3375 【模板】KMP字符串匹配
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
string t,p;
int f[100005],endst;
void prework(string s)
{
int len = s.size();
f[1] = 0;int k = 0;
for(int i = 2;i <= len;i ++)
{
while(k>0&&s[k]!=s[i-1]) k = f[k];
if(s[k] == s[i-1]) k ++;
f[i] = k;
}
return ;
}
void match(string str,string pat)
{
int len = str.size();
int j = 0;
for(int i = 1;i <= len;i ++)
{
while(j > 0 && str[i-1] != pat[j]) j = f[j];
if(pat[j] == str[i-1]) j ++;
if(j == endst)
{
printf("%d\n",i-endst+1);
j = f[j];
}
}
return ;
}
int main()
{
cin >> t;
cin >> p;
endst = p.size();
prework(p);
match(t,p);
for(int i = 1;i <= p.size();i ++) printf("%d ",f[i]);
printf("\n");
return 0;
}
P1470 [USACO2.3]最长前缀 Longest Prefix
P1470 [USACO2.3]最长前缀 Longest Prefix
哈希匹配+DP
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const ull modnum = 91815541;
string s,ls;
int cnt,f[200005],m,ans;
ull base = 17,mypow[200005],h2[200005],tmp;
struct mystr{
ull hkey;
int len;
string stri;
}st[205];
bool cmp(mystr a,mystr b)
{
return a.len < b.len;
}
ull H(string str)
{
ull num = 0;
for(int i = 0;i < str.size();i ++)
num = num*base + ull(str[i]);
return num;
}
int main()
{
mypow[0] = 1;
for(int i = 1;i <= 200001;i ++)
mypow[i] = mypow[i-1]*base;
for(;;)
{
cin >> s;
if(s == ".") break;
if(s.size() > 0)
{
cnt ++;
st[cnt].len = s.size();
st[cnt].hkey = H(s);
}
}
sort(st+1,st+1+cnt,cmp);
while(cin >> s)
{
ls += s;
}
m = ls.size();
for(int i = 0;i < ls.size();i ++)
h2[i+1] = h2[i]*base + ull(ls[i]);
for(int i = 0;i < m;i ++)
{
if(i == 0 || f[i] > 0)
{
for(int j = 1;j <= cnt;j ++)
{
if(st[j].len + i <= m)
{
tmp = h2[i+st[j].len] - h2[i]*mypow[st[j].len];
if(st[j].hkey == tmp)
f[i+st[j].len] = i + st[j].len;
}
}
}
}
for(int i = 1;i <= m;i ++)
ans = max(ans,f[i]);
printf("%d",ans);
return 0;
}
P1381 单词背诵
P1381 单词背诵
哈希表+尺取法
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int modn = 1007;
int base = 17;
int n,m,cnt[100005],tmpha,now;
int head[1009],ha[1005],nxt[1005],ans1,seq[100005],cnter,ans2=99999999,lt,rt,bo[1005];
bool vis[1005];
string s[1005],str;
int GetHash(string s)//计算哈希值
{
int res = 0;
for(int i = 0;i < s.size();i ++)
res = (res*base+s[i])%modn;
return res;
}
void AddHash(int h,int pos)//制作哈希表
{
if(!head[h])
{
head[h] = pos;
return ;
}
nxt[pos] = head[h];
head[h] = pos;
return ;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
cin >> s[i];
ha[i] = GetHash(s[i]);//计算哈希值
AddHash(ha[i],i);//制作哈希表
}
scanf("%d",&m);
for(int i = 1;i <= m;i ++)
{
cin >> str;cnt[i] = cnt[i-1];
tmpha = GetHash(str);//获取哈希值
//查询哈希表
if(head[tmpha] > 0)
{
now = head[tmpha];
if(s[now] == str)
{
seq[i] = now; //记录序列
if(vis[now] == 0)//第一次查到这个单词
{
vis[now] = 1;
ans1 ++;
}
}
while(nxt[now])
{
now = nxt[now];//哈希值重复
if(s[now] == str)
{
seq[i] = now;
if(vis[now] == 0)
{
vis[now] = 1;
ans1 ++;
}
else break;
}
}
}
}
if(ans1 == 0)
{
printf("0\n0");
return 0;
}
cnter = ans1;lt = 1;rt = 1;
while(1)
{
if(cnter == 0)
{
while(seq[lt] == 0 || bo[seq[lt]] > 1)
{
if(bo[seq[lt]] > 1) bo[seq[lt]] --;
lt ++;
if(lt == m + 1) break;
}
if(lt == m + 1) break;
ans2 = min(ans2,rt-lt);
bo[seq[lt]] --;
cnter ++;
lt ++;
}
else
{
if(rt == m + 1) break;
if(seq[rt] > 0)
{
if(bo[seq[rt]] == 0) cnter --;
bo[seq[rt]] ++;
}
rt ++;
}
}
printf("%d\n%d",ans1,ans2);
return 0;
}
P2679 子串
P2679 子串
计数 DP
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int modnum = 1000000007;
string s1,s2;
int n,m,maxk,v,q;
int f[205][205][2][2],ans;
int main()
{
scanf("%d%d%d",&n,&m,&maxk);
cin >> s1 >> s2;
f[0][0][0][0] = 1;
f[0][0][1][0] = 1;
for(int j = 1;j <= n;j ++)
{
for(int i = 1;i <= m;i ++)
{
for(int k = 1;k <= maxk;k ++)
{
if(s1[j-1] == s2[i-1])
f[k][i][j%2][1] = ((f[k-1][i-1][(j-1)%2][0]%modnum + f[k][i-1][(j-1)%2][1]%modnum)%modnum + f[k-1][i-1][(j-1)%2][1]%modnum)%modnum;
else
f[k][i][j%2][1] = 0;
f[k][i][j%2][0] = (f[k][i][(j-1)%2][0]%modnum + f[k][i][(j-1)%2][1]%modnum)%modnum;
f[k][i][j%2][1] %= modnum;
f[k][i][j%2][0] %= modnum;
}
}
}
ans = (f[maxk][m][n%2][0]%modnum + f[maxk][m][n%2][1]%modnum)%modnum;
ans %= modnum;
printf("%d",ans%modnum);
return 0;
}
P3618 误会
P3618 误会
字符串匹配+DP
计数 DP
对于理解到当前字符
i
i
i,有可能加入字符
i
i
i 后可以多一种理解意思,也可以不理解,从前面的情况继承理解意思
设
f
(
i
)
f(i)
f(i) 表示前
i
i
i 个字符组成的字符串能理解的意思种数
f
(
i
)
=
[
T
i
−
l
e
n
2...
i
=
P
]
×
f
(
i
−
l
e
n
2
)
+
f
(
i
−
1
)
f(i) = [T_{i-len2...i}=P]\times f(i-len2) + f(i-1)
f(i)=[Ti−len2...i=P]×f(i−len2)+f(i−1)
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
typedef unsigned long long ull;
const ull modn = 1000000007;
const ull mod1 = 1e9;
int n;
string s1,s2;
ull h1[100005],h2,b = 17,mypow[100005];
ull f[100005],len1,len2;
void solve(int num)
{
len1 = s1.size();len2 = s2.size();
// h1[0] = 0;
for(int i = 0;i < len1;i ++)
{
// h1[i+1] = (h1[i]*b + ull(s1[i]))%mod1;
f[i+1] = 0;
}
// h2 = 0;
// for(int i = 0;i < len2;i ++)
// h2 = (h2*b + ull(s2[i]))%mod1;
f[0] = 1;
for(int i = 1;i <= len1;i ++)
{
if(i >= len2)
if(/*h1[i]-(h1[i-len2]*mypow[len2])%mod1 == h2*/s1.substr(i-len2,len2) == s2)
f[i] = (f[i] + f[i-len2]%modn)%modn;
f[i] = (f[i] + f[i-1]%modn)%modn;
}
printf("Case #%d: %llu\n",num,f[len1]%modn);
}
int main()
{
mypow[0] = 1;
for(int i = 1;i <= 100000;i ++)
mypow[i] = mypow[i-1]*b%mod1;
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
cin >> s1 >> s2;
solve(i);
}
return 0;
}
动态规划
CF730J Bottles
CF730J Bottles
背包DP
对于第一问,可以用贪心解决。
对于第二问要用 DP。
容易发现,在确定了最少要
k
k
k 个瓶子后,如果要求最少的转移时间,那么一定是求瓶中原来水的总和最多的组合,即:
t
min
=
s
−
∑
i
∈
A
a
i
t_{\min} = s - \sum_{i\in A} a_i
tmin=s−i∈A∑ai
其中,
s
s
s 表示水的总量,
A
A
A 表示答案瓶子集合,
∣
A
∣
=
k
|A|=k
∣A∣=k
但是,如果直接 DP 时间的话,那么难以确定是否满足最少瓶子的要求,所以我们同时做两个 DP。
设
f
1
(
i
)
f_1(i)
f1(i) 表示装
i
i
i 单位水所需要的最少瓶子数,则:
f
1
(
i
)
=
min
b
j
≤
i
{
f
1
(
i
)
,
f
1
(
i
−
b
j
)
+
1
}
f_1(i) = \min_{b_j\le i}\{f_1(i),f_1(i-b_j)+1\}
f1(i)=bj≤imin{f1(i),f1(i−bj)+1}
设
f
2
(
i
)
f_2(i)
f2(i) 表示装
i
i
i 单位水所需的最少转移时间。
如果对于某个
f
1
(
i
)
f_1(i)
f1(i) 得到更新,那么同时更新时间:
f
2
(
i
)
=
f
2
(
i
−
b
j
)
+
a
j
f_2(i) = f_2(i-b_j) + a_j
f2(i)=f2(i−bj)+aj
如果计算出来的值等于当前最少瓶子数,那么更新时间:
f
2
(
i
)
=
min
{
f
2
(
i
)
,
f
2
(
i
−
b
j
)
+
a
j
}
f_2(i) = \min\{f_2(i),f_2(i-b_j)+a_j\}
f2(i)=min{f2(i),f2(i−bj)+aj}
最后求出
f
1
m
i
n
f_{1min}
f1min 和
f
2
m
a
x
f_{2max}
f2max ,第二个答案即为
s
−
f
2
m
a
x
s-f_{2max}
s−f2max
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int inf = 0xfffffff;
int n,totw;
int a[150],b[150],ans;
int sum,mink,t[15555],k[15555];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&a[i]);
totw += a[i];
}
for(int i = 1;i <= n;i ++) scanf("%d",&b[i]);
ans = 0;
for(int i = 1;i <= totw+200;i ++) k[i] = inf;
for(int i = 1;i <= n;i ++)
{
for(int j = totw+200;j >= b[i];j --)
{
if(k[j] > k[j-b[i]]+1)
{
k[j] = k[j-b[i]] + 1;
t[j] = t[j-b[i]]+a[i];
}
else
if(k[j] == k[j-b[i]]+1) t[j] = max(t[j],t[j-b[i]]+a[i]);
}
}
k[0] = n;
for(int i = totw+200;i >= totw;i --)
{
if(k[ans] > k[i]) ans = i;
else if(k[ans] == k[i] && t[ans] < t[i]) ans = i;
}
printf("%d %d",k[ans],totw - t[ans]);
return 0;
}
P3572 [POI2014]PTA-Little Bird
P3572 [POI2014]PTA-Little Bird
单调队列优化DP
容易得到:
设
f
(
i
)
f(i)
f(i) 表示在第
i
i
i 棵树上的最小劳累值,则:
f
(
i
)
=
min
i
−
k
≤
j
<
i
{
f
(
i
)
,
f
(
j
)
+
[
h
(
j
)
≤
h
(
i
)
]
}
f(i) = \min_{i-k\le j<i}\{f(i),f(j)+[h(j)\le h(i)]\}
f(i)=i−k≤j<imin{f(i),f(j)+[h(j)≤h(i)]}
但是由于题目的数据范围较大,所以朴素 DP 会超时,观察到式子中求连续定区间最值的特征,想到单调队列优化。
对于当前
f
(
i
)
f(i)
f(i) ,一定从某个
f
(
j
)
,
j
∈
[
i
−
k
,
i
−
1
]
f(j),j\in[i-k,i-1]
f(j),j∈[i−k,i−1] 转移而来,为了保证
f
(
i
)
f(i)
f(i) 的最优解,固然要求区间中的
f
m
i
n
f_{min}
fmin ,如果
f
m
i
n
f_{min}
fmin 对应的
j
j
j 唯一,那么从
f
m
i
n
f_{min}
fmin 转移到
f
(
i
)
f(i)
f(i),一定能使
f
(
i
)
f(i)
f(i) 取得最优解。
证明:
设 j ∈ [ i − k , i − 1 ] j\in[i-k,i-1] j∈[i−k,i−1] 取得 f ( j ) = min x ∈ [ i − k , i − 1 ] { f ( x ) } f(j)=\min_{x\in[i-k,i-1]}\{f(x)\} f(j)=minx∈[i−k,i−1]{f(x)}
如果 a j > a i a_j > a_i aj>ai,显然 f ( i ) = f ( j ) f(i) = f(j) f(i)=f(j) 必定是最优解
如果 a j ≤ a i a_j \le a_i aj≤ai,可能 ∃ j ′ ∈ [ i − k , i − 1 ] , f ( j ′ ) = f ( j ) + 1 \exist \ j'\in[i-k,i-1],f(j')=f(j)+1 ∃ j′∈[i−k,i−1],f(j′)=f(j)+1,此时显然 f ( i ) = f ( j ) f(i)=f(j) f(i)=f(j) 仍是最优解
如果
f
m
i
n
f_{min}
fmin 对应的
j
j
j 不唯一,显然要保证
a
j
≥
a
i
a_j \ge a_i
aj≥ai
由此得到单调队列中维护元素的条件。
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf = 0xfffffff;
int n,a[1000005],q;
int qu[1000005],pos[1000005],head,tail,k;
int f[1000005];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
scanf("%d",&q);
for(int t = 1;t <= q;t ++)
{
scanf("%d",&k);f[1] = 0;head = tail = 1;pos[tail] = 1;
for(int i = 2;i <= n;i ++)
{
while(pos[head] + k < i && head <= tail) head ++;//在区间外
if(a[pos[head]] > a[i]) f[i] = f[pos[head]];
else f[i] = f[pos[head]] + 1;//更新
while((f[pos[tail]] > f[i]/*fmin*/||(a[pos[tail]] <= a[i] && f[pos[tail]] == f[i]/*对应的j不唯一的情况*/)) && head <= tail) tail --;
tail ++;pos[tail] = i;//更新队列
// for(int j = max(1,i-k);j < i;j ++)
// {
// if(a[j] > a[i]) f[i] = min(f[i],f[j]);
// else f[i] = min(f[i],f[j] + 1);
// }
}
printf("%d\n",f[n]);
}
return 0;
}
P5322 [BJOI2019]排兵布阵
P5322 [BJOI2019]排兵布阵
分组背包+一点点小优化
#include<iostream>
#include<cstdio>
using namespace std;
int s,n,m;
long long a[150][150],sum;
long long f[150][20005],ans;
int main()
{
scanf("%d%d%d",&s,&n,&m);
for(int i = 1;i <= s;i ++)
{
for(int j = 1;j <= n;j ++)
{
scanf("%lld",&a[i][j]);
a[i][j] <<= 1;
}
}
for(int i = 1;i <= n;i ++) a[0][i] = -1;
for(int i = 1;i <= n;i ++)
{
for(int k = 0;k <= s;k ++)
{
sum = 0;
for(int p = 1;p <= s;p ++)
if(a[p][i] < a[k][i]+1)
sum += i;
for(int j = m;j >= a[k][i]+1;j --)
{
f[i][j] = max(f[i][j],f[i-1][j-a[k][i]-1]+sum);
ans = max(ans,f[i][j]);
}
}
}
printf("%lld",ans);
return 0;
}
P1833 樱花
P1833 樱花
二进制优化
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int inf = 0xfffffff;
string s,e;
int n,t[10005],c[10005],p[10005];
int tot,f[1005],maxn,ans;
void Gettot(string s1,string s2)
{
int h1 = 0;int m1 = 0;
int now = 0;
while(1)
{
if(s1[now] == ':')
{
now ++;
break;
}
h1 = h1*10 + (s1[now]-'0');
now ++;
}
while(now < s1.size())
{
m1 = m1*10 + (s1[now]-'0');
now ++;
}
now = 0;
int h2 = 0;int m2 = 0;
while(1)
{
if(s2[now] == ':')
{
now ++;
break;
}
h2 = h2*10 + (s2[now]-'0');
now ++;
}
while(now < s2.size())
{
m2 = m2 * 10 + (s2[now] - '0');
now ++;
}
if(m1 > m2)
{
m2 += 60;
h2 --;
}
tot = m2 - m1;
tot += (h2-h1)*60;
return ;
}
int main()
{
cin >> s >> e;
Gettot(s,e);
// cout << tot << endl;
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d%d%d",&t[i],&c[i],&p[i]);
if(p[i] == 0) p[i] = tot/t[i];
}
for(int i = 1;i <= tot;i ++) f[i] = -inf;
for(int i = 1;i <= p[1];i ++) f[i*t[1]] = i*c[1];
for(int i = 2;i <= n;i ++)
{
maxn = min(p[i],tot/t[i]);
for(int j = 1;maxn > 0;j <<= 1)
{
if(j > maxn) j = maxn;
maxn -= j;
for(int k = tot;k >= j*t[i];k --)
{
f[k] = max(f[k],f[k-j*t[i]]+j*c[i]);
ans = max(ans,f[k]);
}
}
}
printf("%d",ans);
return 0;
}
P2851 [USACO06DEC]The Fewest Coins G
P2851 [USACO06DEC]The Fewest Coins G
多重背包
支付者跑有限
找零者跑无限
求证明 DP 上限为
t
+
v
m
a
x
2
t+v_{max}^2
t+vmax2
A
n
s
=
min
{
f
(
t
+
d
)
+
g
(
d
)
}
Ans = \min\{f(t+d)+g(d)\}
Ans=min{f(t+d)+g(d)}
#include<iostream>
#include<cstdio>
using namespace std;
const int inf = 0xfffffff;
int n,t;
int c[105],v[105];
int f[6666666],maxn,last;
int g[6666666],ans;
int main()
{
scanf("%d%d",&n,&t);
maxn = t;
for(int i = 1;i <= n;i ++)
{
scanf("%d",&v[i]);
maxn += v[i];
}
for(int i = 1;i <= n;i ++)
{
scanf("%d",&c[i]);
// maxn += c[i]*v[i];
}
for(int i = 1;i <= 6666660;i ++) f[i] = inf;
for(int i = 1;i <= 6666660;i ++) g[i] = inf;
for(int i = 1;i <= c[1] && i*v[1] <= maxn;i ++) f[i*v[1]] = i;
for(int i = 2;i <= n;i ++)
{
last = min(c[i],maxn/v[i]);
for(int j = 1;last > 0;j <<= 1)
{
if(j > last) j = last;
last -= j;
for(int k = maxn;k >= j*v[i];k --)
f[k] = min(f[k],f[k-j*v[i]]+j);
}
}
for(int i = 1;i*v[1] <= maxn-t;i ++) g[i*v[1]] = i;
for(int i = 2;i <= n;i ++)
{
last = (maxn-t)/v[i];
for(int j = 1;last > 0;j <<= 1)
{
if(j > last) j = last;
last -= j;
for(int k = maxn-t;k >= j*v[i];k --)
g[k] = min(g[k],g[k-j*v[i]]+j);
}
}
ans = inf;
for(int d = 0;d <= maxn-t;d ++)
ans = min(f[d+t]+g[d],ans);
if(ans == inf) printf("-1");
else printf("%d",ans);
return 0;
}
二分答案
P2678 跳石头
#include<iostream>
#include<cstdio>
using namespace std;
int maxlen,n,m;
int rock[50005],sum[50005];
int l,r,mid,cnt,lastrock,ans;
int main()
{
scanf("%d%d%d",&maxlen,&n,&m);
for(int i = 1;i <= n;i ++)
scanf("%d",&rock[i]);
rock[n+1] = maxlen;
l = 0,r = maxlen;
while(l <= r)
{
mid = (l+r)/2;
cnt = 0;lastrock = 0;
for(int i = 1;i <= n;i ++)
{
if(rock[i]-rock[lastrock] < mid)
cnt ++;
else lastrock = i;
}
if(cnt <= m) ans = max(ans,mid);
if(cnt > m)
r = mid - 1;
else
l = mid + 1;
}
printf("%d",ans);
return 0;
}
P1902 刺杀大使
P1902 刺杀大使
二分答案+搜索
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
using namespace std;
const int inf = 0xfffffff;
int n,m,dx[5] = {0,0,0,1,-1},dy[5] = {0,1,-1,0,0};
int map[1005][1005];
int maxn,l,r,mid,ans = inf;
bool flag,vis[1005][1005];
bool bfs(int maxhurt)
{
int nowx = 0;int nowy = 0;
int nexx = 0;int nexy = 0;
queue<int > qx,qy;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
vis[i][j] = 0;
qx.push(1);qy.push(1);
vis[1][1] = 1;
while(!qx.empty())
{
nowx = qx.front();
if(nowx == n) return 1;
nowy = qy.front();
qx.pop();qy.pop();
for(int i = 1;i <= 4;i ++)
{
nexx = nowx + dx[i];
nexy = nowy + dy[i];
if(nexx > 0 && nexy > 0 && nexx <= n && nexy <= m)
{
if(!vis[nexx][nexy] && map[nexx][nexy] <= maxhurt)
{
vis[nexx][nexy] = 1;
qx.push(nexx);qy.push(nexy);
}
}
}
}
return 0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)
{
for(int j = 1;j <= m;j ++)
{
scanf("%d",&map[i][j]);
maxn = max(maxn,map[i][j]);
}
}
l = 0,r = maxn,mid = 0;
while(l <= r)
{
mid = (l+r)/2;
flag = bfs(mid);
if(flag)
{
r = mid - 1;
ans = min(ans,mid);
}
else l = mid + 1;
}
printf("%d",ans);
return 0;
}
P1314 聪明的质监员
P1314 聪明的质监员
二分答案+前缀和+数学技巧
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long inf = 0xffffffffff;
typedef long long ll;
ll n,m,s,li[200005],ri[200005],biopt,bfopt;
ll w[200005],v[200005];
ll cnt[200005],presum[200005];
ll l,mid,r,maxw,ans = inf,sum,newans,sum2;
ll myabs(ll x)
{
if(x < 0) return -x;
return x;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&s);
for(int i = 1;i <= n;i ++)
{
scanf("%lld%lld",&w[i],&v[i]);
maxw = max(maxw,w[i]);
}
for(int i = 1;i <= m;i ++)
{
scanf("%lld%lld",&li[i],&ri[i]);
}
l = 0;mid = 0;r = maxw;
while(l <= r)
{
mid = (l+r)/2;
sum = 0;
for(int i = 1;i <= n;i ++)
{
if(w[i] >= mid)
{
cnt[i] = cnt[i-1] + 1;
presum[i] = presum[i-1] + v[i];
}
else
{
cnt[i] = cnt[i-1];
presum[i] = presum[i-1];
}
}
for(int i = 1;i <= m;i ++)
sum += (cnt[ri[i]]-cnt[li[i]-1])*(presum[ri[i]]-presum[li[i]-1]);
newans = myabs(sum-s);
ans = min(ans,newans);
if(sum > s) l = mid + 1;
else r = mid - 1;
}
// printf("%lld\n",inf);
printf("%lld\n",ans);
return 0;
}
P4343 [SHOI2015]自动刷题机
P4343 [SHOI2015]自动刷题机
二分
注意事项:
l
l
l 和
r
r
r 的初始值问题!!!
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const long long inf = 0xffffffffff;
ll l,k;
ll a[100005],sum,cnt;
ll lt,rt,mid,maxa=0,mina=inf;
ll maxn=0,minn=inf;
int main()
{
scanf("%lld%lld",&l,&k);
for(int i = 1;i <= l;i ++)
{
scanf("%lld",&a[i]);
if(a[i] >= 0)
{
mina = min(mina,a[i]);
maxa = max(maxa,a[i]);
}
}
lt = 1;rt = inf;
while(lt <= rt)
{
mid = (lt+rt)>>1;
sum = 0;cnt = 0;
for(int i = 1;i <= l;i ++)
{
sum += a[i];
sum = max(ll(0),sum);
if(sum >= mid)
{
sum = 0;
cnt ++;
}
}
if(cnt == k) minn = min(minn,mid);
if(cnt > k)
lt = mid + 1;
else
rt = mid - 1;
}
if(minn == inf)
{
printf("-1");
return 0;
}
lt = 1;rt = inf;
while(lt <= rt)
{
mid = (lt+rt)>>1;
sum = 0;cnt = 0;
for(int i = 1;i <= l;i ++)
{
sum += a[i];
sum = max(ll(0),sum);
if(sum >= mid)
{
sum = 0;
cnt ++;
}
}
if(cnt == k) maxn = max(maxn,mid);
if(cnt >= k)
lt = mid + 1;
else
rt = mid - 1;
}
if(maxn == 0) printf("-1");
else printf("%lld %lld",minn,maxn);
return 0;
}
/*
9 4
4
7
-3
5
-7
8
3
-4
9
*/