題目鏈接
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
*/