前言
所以說這次是 HZOI 多校聯測巔峰????(題目,資料過水??)
T1 石子合併
解題思路
簽到題。
發現我們可以給每個數字附一個正負號,每個數字的貢獻就是它本身乘上這個符號。
發現至少應該有一個正號一個負號,直接記錄是否正負數都有,再判斷一下有 0 或者只有一個數的情況。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e6+10,INF=1e18;
int T,minn,n,ans,s[N];
void solve()
{
n=read(); minn=INF; ans=0; bool jud1=false,jud2=false,jud3=false;
for(int i=1;i<=n;i++) s[i]=read(),jud1|=s[i]<0,jud2|=s[i]>0,jud3|=s[i]==0;
if(n==1) return printf("%lld\n",s[1]),void();
if((jud1&&jud2)||jud3){for(int i=1;i<=n;i++)ans+=abs(s[i]);return printf("%lld\n",ans),void();}
for(int i=1;i<=n;i++) ans+=abs(s[i]),minn=min(minn,abs(s[i]));
printf("%lld\n",ans-2*minn);
}
#undef int
int main()
{
#define int long long
freopen("stone.in","r",stdin); freopen("stone.out","w",stdout);
T=read(); while(T--) solve();
return 0;
}
T2 鋪地毯
解題思路
簽到題。
假設一個矩形的左下角是 \((x,y)\) , 右下角是 \((x_2,y_2)\) 。
那麼若干個矩形的公共部分的左下角的橫縱座標就是所有矩形的 \(x\) 或者 \(y\) 的最大值,右上角座標則是最小值。
那麼我們很容易算出 \(n\) 個矩形的公共部分,然後考慮刪掉一個矩形。
這個的話可以記錄一個最大值次大值來很快的實現。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define fi first
#define se second
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e5+10,INF=1e18;
int T,n,ans,all;
struct Node{int x,y,x2,y2;}rec,s[N];
pair<int,int> x,y,x2,y2;
inline void upd1(pair<int,int> &temp,int val)
{
if(val<temp.fi) temp.se=temp.fi,temp.fi=val;
else temp.se=min(temp.se,val);
}
inline void upd2(pair<int,int> &temp,int val)
{
if(val>temp.fi) temp.se=temp.fi,temp.fi=val;
else temp.se=max(temp.se,val);
}
inline int work(pair<int,int> &temp,int val)
{
if(temp.fi==val) return temp.se;
return temp.fi;
}
inline int get(Node temp)
{
int tx=work(x,temp.x),ty=work(y,temp.y),tx2=work(x2,temp.x2),ty2=work(y2,temp.y2);
if(tx>=tx2||ty>=ty2) return 0;
return (tx2-tx)*(ty2-ty);
}
void solve()
{
read(); read(); n=read(); ans=0; x.fi=x.se=-INF; y.fi=y.se=-INF; x2.fi=x2.se=INF; y2.fi=y2.se=INF;
for(int i=1;i<=n;i++) s[i].x=read(),s[i].y=read(),s[i].x2=read(),s[i].y2=read();
for(int i=1;i<=n;i++) upd1(x2,s[i].x2),upd1(y2,s[i].y2),upd2(x,s[i].x),upd2(y,s[i].y);
ans=all=get((Node){INF,INF,-INF,-INF});
if(n!=1) for(int i=1;i<=n;i++) ans+=get(s[i])-all;
printf("%lld\n",ans);
}
#undef int
int main()
{
#define int long long
freopen("carpet.in","r",stdin); freopen("carpet.out","w",stdout);
T=read(); while(T--) solve();
return 0;
}
T3 優美的旋律
解題思路
簽到題。
直接暴力列舉重複的串,然後往後翻,Hash 優化,複雜度嚴格小於 \(n^2ln\)
如果記憶一下的話可能就是 \(n^2\) 了。
對於 \(3000\times 3000\) 同一種字元的極限資料的話最多也就是向後翻大約 \(3\times 10^7\) 次。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e3+10;
const ull base=13331;
int n,a,b,ans;
ull has[N],p[N];
char s[N];
bool vis[N][N];
inline ull get(int l,int r){return has[r]-has[l-1]*p[r-l+1];}
#undef int
int main()
{
#define int long long
freopen("melody.in","r",stdin); freopen("melody.out","w",stdout);
a=read(); b=read(); scanf("%s",s+1); n=strlen(s+1); p[0]=1;
for(int i=1;i<=n;i++) has[i]=has[i-1]*base+s[i],p[i]=p[i-1]*base;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
int len=j-i+1,num=1; ull temp=get(i,j);
while(i+(num+1)*len-1<=n&&get(i+num*len,i+(num+1)*len-1)==temp) num++;
if(num!=1) ans=max(ans,a*len+b*num);
}
printf("%lld",ans);
return 0;
}
T4 基站建設
解題思路
簽到題。
\(n^4\) 的就不說了,我們發現可以列舉一條邊,然後分別列舉兩個點向外面的連邊找公共部分,複雜度就是 \(m^3\)
然後我們可以掃一個點的連邊然後查詢另一個點的連邊是否有這個點 \(m^2log \sim m^2\)
我們可以列舉度數較小的點,這樣的複雜度是 \(m\sqrt{m}\) 的。
顯然複雜度最劣的情況是兩個點的度數相同,假設有 \(x\) 的點。 那麼複雜度就是 \(\dfrac{m^2}{x}\)
\(x\) 的最小值只能是 \(\sqrt{m}\) 級別的,那麼得證複雜度是 \(m\sqrt{m}\) 。
可以通過 bitset
來實現,然而我考場上是根據標號拍完序之後暴掃的,複雜度就不能保證了(逃
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e4+10,M=2e5+10,INF=1e18;
int n,m,ans,s[N];
pair<int,int> p[M];
vector<int> v[N];
#undef int
int main()
{
#define int long long
freopen("station.in","r",stdin); freopen("station.out","w",stdout);
n=read(); m=read();
for(int i=1;i<=n;i++) s[i]=read();
for(int i=1,x,y;i<=m;i++)
x=read(),y=read(),p[i]=make_pair(x,y),
v[x].push_back(y),v[y].push_back(x);
for(int i=1;i<=n;i++) sort(v[i].begin(),v[i].end());
for(int i=1;i<=m;i++)
{
int x=p[i].first,y=p[i].second,mx=-INF,sec=-INF;
if(v[x].size()<v[y].size())
{
int pos=0,lim=v[y].size()-1;
for(auto it:v[x])
{
while(pos<lim&&v[y][pos]<it) pos++;
if(it==y||v[y][pos]!=it) continue;
if(s[it]>mx) sec=mx,mx=s[it];
else sec=max(sec,s[it]);
}
}
else
{
int pos=0,lim=v[x].size()-1;
for(auto it:v[y])
{
while(pos<lim&&v[x][pos]<it) pos++;
if(it==x||v[x][pos]!=it) continue;
if(s[it]>mx) sec=mx,mx=s[it];
else sec=max(sec,s[it]);
}
}
if(sec!=-INF&&mx!=-INF) ans=max(ans,(s[x]+1)*(s[y]+1)+mx*sec);
}
printf("%lld",ans);
return 0;
}