题目链接
D Make The Fence Great Again(DP)
题意:
有 个数字,每次操作可以将一个数字加 ,并且需要代价 ,问要使得相邻的数字不同最少需要的代价。
思路:
因为只是要求相邻的数字不同,那么每一段相同的数字只要间隔给数字加 ,在段与段之间最多再加上 ,所以每一个数字最多加两次,那么令 表示使前 个数字满足题意,并且第 个数字加 次的最小费用。就只要判断数字是否相同,转移一下就行。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
int n,q;
ll arr[N],w[N];
ll dp[N][4];
int main()
{
scanf("%d",&q);
while(q--){
scanf("%d",&n);
for(int i=1;i<=n;i++)dp[i][0]=dp[i][1]=dp[i][2]=1e18;
for(int i=1;i<=n;i++)scanf("%lld%lld",&arr[i],&w[i]);
dp[1][0]=0,dp[1][1]=w[1],dp[1][2]=w[1]*2;
for(int i=2;i<=n;i++){
for(int j=0;j<=2;j++){
if(arr[i-1]+j!=arr[i])dp[i][0]=min(dp[i][0],dp[i-1][j]);
}
for(int j=0;j<=2;j++){
if(arr[i-1]+j!=arr[i]+1)dp[i][1]=min(dp[i][1],dp[i-1][j]+w[i]);
}
for(int j=0;j<=2;j++){
if(arr[i-1]+j!=arr[i]+2)dp[i][2]=min(dp[i][2],dp[i-1][j]+2*w[i]);
}
}
ll ans=1e18;
for(int i=0;i<=2;i++)ans=min(ans,dp[n][i]);
cout<<ans<<endl;
}
return 0;
}
E Game With String (思维)
题意:
给定一个字符串 ,只包含 和 ,两个人轮流操作,先手每次必须选择连续的 个 ,把它变成 ,后手则每次要选择b个 ,若不能操作则输,问谁胜。
思路:
预先处理所有的连续 的长度 由于每次只能取固定的长度,而且 所以 若存在 后手必胜, 就是存在两个以上的 后手肯定可以将一个 变成 的情况。 如果不存在 那么 就判断奇偶性即可。 存在一个 那么将其进行拆分分类讨论即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a,b;
int T;
char s[N];
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&a,&b);
scanf("%s",s+1);
int gs1=0,gs2=0,gs3=0,len;
int l=strlen(s+1);
s[l+1]='3';
int temp=0;
for(int i=1;i<=l+1;i++){
//cout<<s[i];
if(s[i]=='.')temp++;
else{
if(temp>=b&&temp<a)gs1++;
if(temp>=a&&temp<2*b)gs2++;
if(temp>=2*b)gs3++,len=temp;
temp=0;
}
}
//puts("");
//cout<<gs1<<" "<<gs2<<" "<<gs3<<endl;
if(gs3>=2||gs1){puts("NO");continue;}
else if(gs3==0){
if(gs2%2==0)puts("NO");
else puts("YES");
continue;
}
else if(gs2%2==1){
if(len>=2*a&&len<=a+3*b-2)puts("YES");
else puts("NO");
continue;
}
else {
if(len<=a+2*b-2||(len>=3*a&&len<=a+4*b-2))puts("YES");
else puts("NO");
continue;
}
}
}
F Choose a Square(线段树)
题意:
在一个平面上有很多点,每一个点有一个权值 ,有正有负,要求一个边长为 的正方形并且对角线在 上,收益为在正方形内的 ,求出收益的最大值,和对应的正方形坐标。
思路:
首先由于正方形的对角线必须在
上,所以我们可以沿着这条线枚举正方形的右上方的定点
,找出使得收益最大的左下方顶点
,
我们可以用
表示从
到
的
之和,
表示从
到
之和,
表示从
到
之和。
那么从
到
总收益为
可以进行移项变成
其中括号中的都是以
为下标那么考虑用线段树维护其最小值,从而使得总收益最大,就是沿着直线
不断更新区间最小值即可,注意每一个点产生影响应该是其坐标的最小值,还要离散化,细节较多。
代码:
代码细节参考这里不过他维护的是最大值
#include <bits/stdc++.h>
#define ll long long
#define lc x<<1
#define rc x<<1|1
using namespace std;
const int N=1e6+10;
int ls[N*4],rs[N*4];
struct node{
int pos;
ll f,mi;
node(int a=0,ll b=0,ll c=0){pos=a,f=b,mi=c;}
}e[N*4];
node operator + (const node a,const node b){//这个操作挺好用
node c;c.mi=min(a.mi,b.mi);
if(a.mi<=b.mi)c.pos=a.pos;
else c.pos=b.pos;
return c;
}
void built(int x,int l,int r){
ls[x]=l;rs[x]=r;
if(l==r){
e[x].pos=l;e[x].f=e[x].mi=0;
return ;
}
int mid=(l+r)/2;
built(lc,l,mid);built(rc,mid+1,r);
e[x]=e[lc]+e[rc];
}
void down(int x){
if(e[x].f==0)return ;
ll f=e[x].f;e[x].f=0;
e[lc].f+=f;e[rc].f+=f;
e[lc].mi+=f;e[rc].mi+=f;
return ;
}
void add(int x,int LL,int RR,ll val){
if(ls[x]>=LL&&rs[x]<=RR){
e[x].mi+=val;e[x].f+=val;
return ;
}
down(x);
int mid=(ls[x]+rs[x])/2;
if(LL<=mid)add(lc,LL,RR,val);
if(RR>mid)add(rc,LL,RR,val);
e[x]=e[lc]+e[rc];
}
node query(int x,int LL,int RR){
if(ls[x]>=LL&&rs[x]<=RR){
return e[x];
}
down(x);
int mid=(ls[x]+rs[x])/2;
if(LL>mid)return query(rc,LL,RR);
else if(RR<=mid)return query(lc,LL,RR);
else return query(lc,LL,RR)+query(rc,LL,RR);
}
vector<ll>v;
int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
vector<int>G[N];
int n;
int x[N],y[N],m,c[N];
int main()
{
scanf("%d",&n);
int x1=1,x2=1;
for(int i=1,a,b;i<=n;i++){
scanf("%d%d%d",&a,&b,&c[i]);
v.push_back(a);v.push_back(b);
x[i]=a;y[i]=b;
}
sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());m=v.size();
for(int i=1;i<=n;i++){
x[i]=getid(x[i]),y[i]=getid(y[i]);
G[max(x[i],y[i])].push_back(i);
}
ll sum=0,ans=-1e18;
built(1,1,m);
for(int i=1;i<=m;i++){
for(int pos:G[i]){
sum+=c[pos];
add(1,min(x[pos],y[pos]),m,c[pos]);//取最小值做为起点
}
if(i!=1){
node temp=query(1,1,i-1);
if(ans<=sum-v[i-1]-temp.mi){
ans=sum-v[i-1]-temp.mi;
x1=v[temp.pos];
x2=v[i-1];
}
}
if(sum-(v[i-1]-v[0])>ans){//wa15,因为坐标轴的贡献在后面加入
ans=sum-(v[i-1]-v[0]);
x1=v[0];
x2=v[i-1];
}
add(1,i,i,-v[i]);
}
if(ans<0){//不加判断小于0会wa
for(int i=1;i<=5e5+1;i++){
int k=getid(i);
if(v[k-1]!=i){
x1=i,x2=i;break;
}
}
ans=0;
}
cout<<ans<<endl;
cout<<x1<<" "<<x1<<" "<<x2<<" "<<x2<<endl;
}
/*
10
10 0 -1
1 10 -4
3 6 3
4 2 -5
10 7 -1
3 7 3
3 7 -2
8 10 4
5 0 -1
2 3 3
3
8 8 10 10
*/