将问题转化为求凸包
#include
using namespace std;
typedef long long ll;
const double esp=1e-10;
const int MAXN=100000+5;
struct Point{
int x,y,id;
Point(int _x=0,int _y=0,int _id=0){
x=_x,y=_y,id=_id;
}
bool operator <(const Point &b)const{
if(x==b.x) return id>1;
if(K(p[i],p[Stack[m]])1&&((p[i]-p[Stack[top-2]])^(p[Stack[top-1]]-p[Stack[top-2]]))<=0)
top--;
Stack[top++]=i;
}
}
}
}c1,c2;
int main(){
int n,m,x,y;
scanf("%d",&n);
for(int i=0;i
思维+二分
#include
using namespace std;
typedef long long LL;
const int maxn = 5e5+1000;
LL N,T;
LL x[maxn],a[maxn],sum[maxn],sumd[maxn];
// sum[i] 表示到第i个点,共有多少product
// sumd[i] 表示到第i个点,把这些点上所有的货物运送到0点需要的cost
// 把[l,r] 区间内的货物移到 l 则 ca1(l,r) = sumd[r]-sumd[l-1]-(sum[r]-sum[l-1])*d[l]
// 把[l,r] 区间内的货物移到 r 则 ca2(l,r) = (sum[r]-sum[l-1])*(x[r]-x[l])-ca1(l,r)
LL ca2(LL l,LL r){
return sumd[r]-sumd[l-1]-(sum[r]-sum[l-1])*x[l];
}
LL ca1(LL l,LL r){
return (sum[r]-sum[l-1])*(x[r]-x[l])-ca2(l,r);
}
bool judge(LL mid){
LL l ,r,i;
LL mid2 = mid/2+1;
// 假设l 为必选的位置,没有完全转移的箱子是 r
l = 1,r = 1, i = 1;
while(1){
while( r <= N&& sum[r]-sum[l-1] < mid) r++;// 不足mid就增加r
while(i <= N&&sum[i]-sum[l-1] < mid2) i++;// 求区间货物中位数所在的位置
if(r > N|| i > r) break;// 如果找不到符合条件的break出去
LL plus = sum[r]-sum[l-1]-mid;// 右端点可能会多plus个product
if((ca1(l,i)+ca2(i,r)-(x[r]-x[i])*plus )<= T) return true;
l++;// 这一个左端点不行,换下一个
}
// 假设r 集装箱内所有物品都已经转移,而l可能没有转移完
l = r = i = N;
while(1){
while(l >= 1&& sum[r]-sum[l-1] < mid) l--;
while(i >= 2&& sum[r]-sum[i-1] < mid2) i--;
if(i < l||l < 1)
break;
LL plus = sum[r]-sum[l-1]-mid;// 左端点可能多plus个product
if(ca1(l,i)+ca2(i,r)-(x[i]-x[l])*plus <= T) return true;
r--;// 这个左端点不行,换下一个
}
return false;
}
int main(void)
{
scanf("%lld %lld",&N,&T),T>>=1;
for(int i = 1;i <= N; ++i)
scanf("%lld",&x[i]);
LL M = 0;
for(int i = 1;i <= N; ++i)
scanf("%lld",&a[i]),sum[i] = sum[i-1]+a[i],M = max(M,a[i]),sumd[i] = sumd[i-1]+a[i]*x[i];
LL l = M,r = sum[N];
while(l <= r){
LL mid = l+(r-l)/2;
if(judge(mid))
l = mid+1;
else
r = mid-1;
}
printf("%lld",r);
return 0;
}
树形dp
#include
#define ll long long
using namespace std;
const int maxn=4e5+10;
const ll mod=1e9+7;
const int maxm=1e6+10;
const double eps=1e-7;
const ll inf=(ll)1e13;
struct Edge{
int u,v,next;
}edge[maxm];
int head[maxn];
int tot=0;
void init(){
memset(head,-1,sizeof(head));
tot=0;
}
void addedge(int u,int v){
edge[tot]=Edge{u,v,head[u]};
head[u]=tot++;
}
int n;
ll a[maxn];
ll dp[maxn][8][4];
//dp(i,j,k)表示i子树上有j条链,i节点上有k条链的结果
void dfs(int u,int fa){
dp[u][0][0]=0;
//啥都没有,肯定是0了
dp[u][1][1]=a[u];
//只有自己一个节点作为一条链的答案
for (int i=head[u];~i;i=edge[i].next){
int v=edge[i].v;
if (v==fa) continue;
dfs(v,u);
for (int j=4;j>=0;j--){
for (int k=j-1;k>=0;k--){
dp[u][j][2]=max(dp[u][j][2],dp[u][k][1]+dp[v][j-k][1]);
//其他子树上提供了k条链,并且有一条在u上,那么剩下的j-k条由v提供,并提供一条u-v的链
dp[u][j][2]=max(dp[u][j][2],dp[u][k][2]+max(dp[v][j-k][0],max(dp[v][j-k+1][2],dp[v][j-k][1])));
//其他子树上提供了k条链,并且有两条在u上,那么剩下的j-k条由v提供,不提供u节点到v的链
//这时候v节点的dp值取0条连在v上,一条连在v上,两条连在v上的dp的最大值
//取两条的时候,数目要+1
dp[u][j][1]=max(dp[u][j][1],dp[u][k][0]+dp[v][j-k][1]+a[u]);
//其他子树上提供了k条链,并且有一条在u上,那么剩下的j-k条由v提供,并提供一条u-v的链
dp[u][j][1]=max(dp[u][j][1],dp[u][k][1]+max(dp[v][j-k][0],max(dp[v][j-k+1][2],dp[v][j-k][1])));
//其他子树上提供了k条链,并且有一条在u上,那么剩下的j-k条由v提供,不提供u节点到v的链
//这时候v节点的dp值取0条连在v上,一条连在v上,两条连在v上的dp的最大值
//取两条的时候,数目要+1
dp[u][j][0]=max(dp[u][j][0],dp[u][k][0]+max(dp[v][j-k][0],max(dp[v][j-k+1][2],dp[v][j-k][1])));
//其他子树上提供了k条链,并且没有在u上,那么剩下的j-k条由v提供,不提供u节点到v的链
//这时候v节点的dp值取0条连在v上,一条连在v上,两条连在v上的dp的最大值
//取两条的时候,数目要+1
}
}
}
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
init();
for (int i=1;i
#include
#include
#include
#include
using namespace std;
int a[100005];
int b[100005];
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
int ans=2*n-n%2;
int f=0;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
if(n&1&&(x==n/2+1||y==n/2+1))
f=1;
if(a[x]==0){
ans--;
a[x]=1;
}
if(b[y]==0){
ans--;
b[y]=1;
}
}
ans+=f;
printf("%d\n",ans);
}
return 0;
}
get两个知识点:
1、随机化过题
2、如果有Q次修改区间值的操作,如何快速求出修改后每个位置的值
随机化的话,首先,对于这题,我们有这么一个想法:修改后的值 = 修改次数 * 初始值,那么这颗植物是很有可能活下来的。显然,直接这么写用脚都能hack掉,比如如果 8 = 4 + 4,那么显然也可以有 8 = 2 + 6, 那么怎么办?随机打乱一下,不妨设4 -> 6, 2 - >3, 6 -> 5,那么上面就变成了 12 = 6 + 6, 8 = 3 + 5,大大降低了被hack的几率。
对于一维的来说,[L,R] 加上k等价于 a[L] += k, a[R + 1] -= k,然后累加之后就是改变了多少值,再加上原先的值就是答案。理由如下,因为它是求的前缀和,那么对于L之后的一定都要加上k,R之后的就不能再加了,所以要减去。二维的话也可以类似的推广过去。
#include
using namespace std;
typedef long long ll;
const int N = 2002000;
ll mp[N], g[N], change[N], vis[N];
int main(){
int n, m, t;
scanf("%d %d %d", &n, &m, &t);
for(int i = 1; i <= n * m; i ++){
mp[i] = i;
}
random_shuffle(mp + 1, mp + n * m + 1);
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
scanf("%lld", &g[i * m + j]);
g[i * m + j] = mp[g[i * m + j]];
}
}
int x1, y1, x2, y2, k;
while(t --){
scanf("%d %d %d %d %d", &x1, &y1, &x2, &y2, &k);
k = mp[k];
change[x1 * m + y1] += k, change[(x2 + 1) * m + y2 + 1] += k;
change[(x2 + 1) * m + y1] -= k, change[x1 * m + y2 + 1] -= k;
vis[x1 * m + y1] ++, vis[(x2 + 1) * m + y2 + 1] ++;
vis[(x2 + 1) * m + y1] --, vis[x1 * m + y2 + 1] --;
}
int tot = 0;
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
change[i * m + j] += change[(i - 1) * m + j] + change[i * m + j - 1] - change[(i - 1) * m + j - 1];
vis[i * m + j] += vis[(i - 1) * m + j] + vis[i * m + j - 1] - vis[(i - 1) * m + j - 1];
if(change[i * m + j] != vis[i * m + j] * g[i * m + j])
tot ++;
}
}
printf("%d\n", tot);
return 0;
}