T1
把修改放到差分序列上做,这样每次修改只需要修改两个位置,且每次修改会对后面的所有温度产生影响
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
int n,m,a[200200];
ll f[200100];
ll s,t,ans;
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void modify(int pos,ll val)
{
if (pos>n) return;
if (f[pos]>0) ans+=s*f[pos];
else ans+=t*f[pos];
f[pos]+=val;
if (f[pos]>0) ans-=s*f[pos];
else ans-=t*f[pos];
}
signed main()
{
n=read();m=read();s=read();t=read();
rep(i,0,n) a[i]=read();
rep(i,1,n) f[i]=a[i]-a[i-1];
ans=0;
rep(i,1,n)
if (f[i]>0) ans-=s*f[i];
else ans-=t*f[i];
while (m--)
{
int l=read(),r=read(),x=read();
modify(l,x);modify(r+1,-x);
printf("%lld
",ans);
}
return 0;
}
T2
不难发现如果按照“快车->准快车->慢车”的顺序坐车一定不会更劣
对于每一段快车区间((s_i,s_{i+1}]),考虑如何求出最大的可到达车站
我们在(s_i)下车后会有一段剩余时间,记作(rst_i),和最开始的结论同理,我们希望在“最后一个可以使用慢车到达的车站”处建立一个准快车车站,使得从(s_i)可以直接到达那里,证明的话考虑往前或往后调整都不会是更优的
这样的话我们就有(O(mk))个可以考虑的准快车建立点,使用优先队列贪心即可
注意一些奇奇怪怪的特判
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define int long long
#define eps 1e-8
int n,m,k,now[5050],s[5050];
ll a,b,c,tot,rst[5050];
priority_queue<int> q;
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
signed main()
{
n=read();m=read();k=read();
scanf("%lld%lld%lld%lld",&a,&b,&c,&tot);
rep(i,1,m)
{
s[i]=read();
rst[i]=tot-b*(s[i]-1);
now[i]=s[i];
}
int ans=0;
//rep(i,1,m) cout << now[i] << " ";cout << endl;
if (b*(s[m]-1)>tot) ans--;
rep(i,1,m-1)
{
if (rst[i]<0) continue;
int nxt=min(s[i+1],s[i]+rst[i]/a+1);
ans+=(nxt-s[i]);
rst[i]-=c*(nxt-s[i]);
now[i]=nxt;
}
rep(i,1,k)
{
rep(j,1,m-1)
{
if (rst[j]<0) continue;
int nxt=min(s[j+1],now[j]+rst[j]/a+1);
q.push(nxt-now[j]);
rst[j]-=c*(nxt-now[j]);
now[j]=nxt;
}
}
rep(i,1,k-m)
{
if (q.empty()) break;
ans+=q.top();q.pop();
}
printf("%lld",ans);
return 0;
}
T3
画一下的话可以发现合法方案就是在四个角上的阶梯形,由于四种情况等价我们可以将矩阵旋转后只处理左下角的阶梯形,最后取个(min)即可
题目中由很明显的二分答案的暗示,但是看起来(check)不太容易
考虑矩阵的全局(max)和(min)值,对于我们二分出来的(mid),我们考虑左下角的阶梯有(geq mid+min),右上角的有(leq max-mid),由于(max)和(min)不会在同一个阶梯里,所以这样做我们一定能考虑到最优解,之后维护一个当前行的右端点指针(单调不降),每行判断一下在左下角不合法的点是否在右上角即可
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 2e9
#define eps 1e-8
int n,m,a[2020][2020],ans=maxd,mx=0,mn=maxd;
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void reverse1()
{
rep(i,1,n/2) rep(j,1,m) swap(a[i][j],a[n-i+1][j]);
}
void reverse2()
{
rep(i,1,n) rep(j,1,m/2) swap(a[i][j],a[i][m-j+1]);
}
bool chk(int mid)
{
int now=0;
rep(i,1,n)
{
rep(j,1,m)
if (a[i][j]<mx-mid) now=max(now,j);
rep(j,1,m)
if ((a[i][j]>mid+mn) && (j<=now)) return 0;
}
return 1;
}
void solve()
{
int l=0,r=mx-mn,now=0;
while (l<=r)
{
int mid=(l+r)>>1;
if (chk(mid)) {now=mid;r=mid-1;}
else l=mid+1;
}
ans=min(ans,now);
}
int main()
{
n=read();m=read();
rep(i,1,n) rep(j,1,m)
{
a[i][j]=read();
mx=max(a[i][j],mx);
mn=min(a[i][j],mn);
}
solve();
reverse1();
solve();
reverse2();
solve();
reverse1();
solve();
printf("%d",ans);
return 0;
}
T4
对于每名球员,最优策略一定是:跑到某个位置接球->带着球跑->踢出
也就是说当一个球到达某个点的时候,一定是一个距离它最近的人跑过去捡起它
接下来考虑拆点跑最短路,((x,y,0-3))表示一个球在往四个方向走,((x,y,4))表示有人在((x,y))控球
首先球往四个方向跑的时候花费是(a),之后一个人在控球状态下如果将球提出可以看做(4)向(0-3)连了一条长度为(b)的边,之后带球跑动便连(c)的边,最后如果一个球在某一个点要想找一个控球人的话,就是离它最近的人的距离( imes c),这个可以通过预先bfs实现
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
const int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
struct node{int to,nxt;ll cost;}sq[5000100];
int all=0,head[1501000];
struct hnode{int u;ll dis;};
bool operator <(hnode p,hnode q) {return p.dis>q.dis;}
struct pnode{int x,y;};
queue<pnode> q;
int h,w,n,id[510][510][5],a,b,c,tot=0;
ll d[510][510],dis[1500010];
bool vis[1500010],in[505][505];
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void add(int u,int v,ll w)
{
all++;sq[all].to=v;sq[all].nxt=head[u];sq[all].cost=w;head[u]=all;
}
void bfs()
{
while (!q.empty())
{
pnode now=q.front();q.pop();
rep(i,0,3)
{
int nx=now.x+dx[i],ny=now.y+dy[i];
if ((nx<1) || (nx>h) || (ny<1) || (ny>w)) continue;
if (d[nx][ny]>d[now.x][now.y]+c)
{
d[nx][ny]=d[now.x][now.y]+c;
q.push((pnode){nx,ny});
}
}
}
}
void dij(int st)
{
priority_queue<hnode> q;
while (!q.empty()) q.pop();
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[st]=0;q.push((hnode){st,0});
while (!q.empty())
{
int u=q.top().u;q.pop();
if (vis[u]) continue;vis[u]=1;
for (int i=head[u];i;i=sq[i].nxt)
{
int v=sq[i].to;
if (dis[v]>dis[u]+sq[i].cost)
{
dis[v]=dis[u]+sq[i].cost;
if (!vis[v]) q.push((hnode){v,dis[v]});
}
}
}
}
int main()
{
h=read()+1;w=read()+1;
a=read();b=read();c=read();
n=read();
rep(i,1,h) rep(j,1,w) rep(k,0,4) id[i][j][k]=(++tot);
//cout << tot << endl;
int stx,sty,edx,edy;
memset(d,0x3f,sizeof(d));
rep(i,1,n)
{
int x=read()+1,y=read()+1;
if (i==1) {stx=x;sty=y;}
if (i==n) {edx=x;edy=y;}
if (!in[x][y]) q.push((pnode){x,y});
in[x][y]=1;d[x][y]=0;
}
bfs();
rep(x,1,h)
rep(y,1,w)
{
rep(k,0,3)
{
add(id[x][y][k],id[x][y][4],d[x][y]);
add(id[x][y][4],id[x][y][k],b);
int nx=x+dx[k],ny=y+dy[k];
if ((nx<1) || (nx>h) || (ny<1) || (ny>w)) continue;
add(id[x][y][k],id[nx][ny][k],a);
add(id[x][y][4],id[nx][ny][4],c);
}
}
dij(id[stx][sty][4]);
printf("%lld
",dis[id[edx][edy][4]]);
return 0;
}
T5
首先显然的是我们可以先完成所有的染色,再进行折叠
将最后的序列还原,不难发现一个合法的序列是有两种颜色的连续段,且除了开头和结尾的两段长度奇偶性任意以外,其它的每一段长度均为偶数
直接做依然不大好做,注意到满足上一条件的某种颜色段的起始位置奇偶性一致,那么我们可以枚举这个奇偶性,之后调整该颜色使其合法(具体如何调整见程序),之后我们可以贪心的选取剩下最多个数的颜色作为第二种颜色。
#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
int n,m,col[1001000],mx,cnt[1001000],cnt1[1001000];
vector<int> pos[1001000];
int read()
{
int x=0,f=1;char ch=getchar();
while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
return x*f;
}
void add(int c)
{
cnt[cnt1[c]]--;cnt1[c]++;cnt[cnt1[c]]++;
if (cnt1[c]>mx) mx=cnt1[c];
}
void dec(int c)
{
cnt[cnt1[c]]--;cnt1[c]--;cnt[cnt1[c]]++;
if (!cnt[mx]) mx--;
}
int main()
{
n=read();m=read();
rep(i,1,n)
{
col[i]=read();add(col[i]);
pos[col[i]].pb(i);
}
rep(i,1,m)
{
int len=pos[i].size();
col[0]=col[n+1]=i;
rep(j,0,len-1)
{
int now=pos[i][j];
dec(i);
}
rep(j,0,len-1)
{
int now=pos[i][j],nxt;
if (now&1) nxt=now-1;else nxt=now+1;
if (col[nxt]!=i) dec(col[nxt]);
}
int ans=n-len-mx;
rep(j,0,len-1)
{
int now=pos[i][j],nxt;
if (now&1) nxt=now-1;else nxt=now+1;
if (col[nxt]!=i) add(col[nxt]);
}
rep(j,0,len-1)
{
int now=pos[i][j],nxt;
if (now&1) nxt=now+1;else nxt=now-1;
if (col[nxt]!=i) dec(col[nxt]);
}
ans=min(ans,n-mx-len);
rep(j,0,len-1)
{
int now=pos[i][j],nxt;
if (now&1) nxt=now+1;else nxt=now-1;
if (col[nxt]!=i) add(col[nxt]);
}
printf("%d
",ans);
rep(j,0,len-1)
{
int now=pos[i][j];
add(i);
}
}
return 0;
}