2020十二月暂记


学习规划
字符串:
哈希
KMP
Trie树

字符串

P3370 【模板】字符串哈希

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字符串匹配

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)=[Tilen2...i=P]×f(ilen2)+f(i1)

#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=siAai
其中, 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)=bjimin{f1(i),f1(ibj)+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(ibj)+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(ibj)+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} sf2max

#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)=ikj<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[ik,i1] 转移而来,为了保证 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[ik,i1] 取得 f ( j ) = min ⁡ x ∈ [ i − k , i − 1 ] { f ( x ) } f(j)=\min_{x\in[i-k,i-1]}\{f(x)\} f(j)=minx[ik,i1]{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 ajai,可能 ∃   j ′ ∈ [ i − k , i − 1 ] , f ( j ′ ) = f ( j ) + 1 \exist \ j'\in[i-k,i-1],f(j')=f(j)+1  j[ik,i1],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 ajai
由此得到单调队列中维护元素的条件。

#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 跳石头

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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值