杂题记录
本人太懒,但是遇到有意思的题又不想放过,所以下面只写思路没有代码。
持续更新中
就是问你有多少个区间满足区间最大值是区间最小值的两倍。
乍一看不太可做,实际上是个二分。
枚举左端点,右端点变大的过程中,区间最大值不会变小,区间最小值不会变大,\(\frac{区间最大值}{区间最小值}\)不会变小,具有二分性。
题目大意:给你一个数组,从里面尽可能多的选数,使得他们模m同余(m>=2)。问你最多能选出来几个。
这题竟然是随机化。。。
如果我们选m=2,可以得到一个大小为n/2的较优解(不一定是最优解),所以可知最优解的大小>=n/2。
我们随便选两个数,它们都在最优解里的概率>=1/4,不能同时在最优解的概率<=3/4,我们可以多选出来几对(假设是k对),这几对都同时不在最优解的概率<=\((\frac{3}{4})^k\),k足够大的时候\((\frac{3}{4})^k\),可以看似成0
对于选出来的两个数,把他们的差质因数分解,分解出来的质数分别看作m,O(n)处理一遍就行,取所有情况里面最大的集合就行。
题目大意:跟上面那个hud7073一样,不过这道题要求选的数是连续的
既然是要求选的数连续的,我们就能想到用双指针。
怎么找m?我之前有个比较nature的想法就是让所有的a减去\(a_1\),但很明显如果\(a_1\)如果不在最后的答案那个区间里的话这种做法是错误的。
实际上只用设\(b_i=a_{i+1}-a_i\),把双指针放在b上,然后快速求个gcd就行,快速求gcd用线段树和st表都行。
不要被题目迷惑,还是要移动羊的,首先把羊向左移动inf。然后每个羊要有如下处理:如果这个羊往右移动x距离会使整体收益变化(相对于往右移动x-1距离)y的收益,那么d[x]+=y,最后按照下标递增累加d,同时存set即可
附上本人比赛的时候写的暴力,相比起来就很难蚌
#include<bits/stdc++.h>
#define int long long
#define DB double
using namespace std;
int T,n,m,now;
const int N=1000010;
int w[N],v[N],x[N],l[N],r[N],posd[N],posx[N];
set<int>ans;
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)scanf("%lld",&w[i]);
for(int i=1;i<=n;++i)scanf("%lld",&v[i]);
for(int i=1;i<=m;++i)scanf("%lld",&x[i]);
sort(x+1,x+1+m);
for(int i=1;i<=m;++i)posx[i]=x[i]-x[m];
for(int i=1;i<=n;++i)l[i]=r[i-1]+1,r[i]=l[i]+w[i]-1;
int bu=1;ans.insert(0);
while(posx[1]<=r[n])
{
int newbu=1e18,sum=0;
for(int i=1;i<=m;++i)
{
posx[i]+=bu;
if(r[n]<posx[i])continue;
if(posx[i]<1)
{
newbu=min(newbu,1-posx[i]);
continue;
}
while(1)
{
if(l[posd[i]]<=posx[i]&&posx[i]<=r[posd[i]])break;
++posd[i];
}
sum+=v[posd[i]];
newbu=min(newbu,r[posd[i]]-posx[i]+1);
}
//cout<<"-> "<<posx[n]<<" "<<sum<<endl;
ans.insert(sum);
bu=newbu;
}
cout<<ans.size();
return 0;
}
/*
3 3
1 2 3
2 -1 3
2 3 4
*/
官方题解链接,这里面有代码
很牛的一道题。我们可以设val(l,r)表示l到r组成合法括号序列时最大收益,这部分还是好求的,一个经典区间DP。
这道题说最后的序列可以不是合法括号序列,所以对于那些不匹配的左右括号,是怎么个状态?
分析一下可以知道,我们最后的得到的括号序列,把合法的部分去掉,剩下的长这个样子))...))(((...(((
所以我们考虑))...))(((...(((的中间这个断点的位置,断点左边我们求如下形式的最大价值(加号表示字符串拼接):))))+合法括号序列+)))+合法括号序列+)))))...
断电右边类似,最后求个max即可
官方题解写的比我好,我直接贴上官方题解
若由起点和终点划定的矩形内部包含至少一家超市则答案即为起点到终点的曼哈顿距离。
否则答案即为曼哈顿距离加矩形最近超市的曼哈顿距离的两倍。
第一种情况可以通过二维前缀和维护。
第二种情况可以先预处理地图上每一点到最近的超市距离(多源 bfs),然后枚举矩形轮廓统计最小值即可(也可使用 st表 维护)。
容斥原理模板题。关于一些理解上面的问题可以看这个
很经典的单调栈题目
最小费用可行流
AT_arc084_b [ABC077D] Small Multiple
我们可以通过不断+1或*10得到一个数,这两种方式位数和分别会增加1和0。同时我们只用考虑倍数关系,所以我们考虑所有数模k,也就是1到k-1这k个点。跑最短路即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int k,ans,S,tot;
const int N=300010;
int head[N],to[N],dis[N],nt[N],val[N];
void add(int x,int y,int z)
{
to[++tot]=y;val[tot]=z;nt[tot]=head[x];head[x]=tot;
}
int suan(int x)
{
int res=0;
while(x)
{
res+=x%10;
x/=10;
}
return res;
}
struct dian
{
int hao,dis;
friend bool operator <(const dian &a,const dian &b)
{
return a.dis>b.dis;
}
};
priority_queue<dian>q;
int vis[N];
void DIJ()
{
for(int i=0;i<k;++i)dis[i]=1e9;
q.push((dian){S,0});dis[S]=0;
while(!q.empty())
{
int x=q.top().hao;q.pop();
if(vis[x])continue;vis[x]=1;
for(int i=head[x];i;i=nt[i])
if(dis[to[i]]>dis[x]+val[i])
{
dis[to[i]]=dis[x]+val[i];
q.push((dian){to[i],dis[to[i]]});
}
}
}
signed main()
{
cin>>k;S=k;
ans=suan(k);
for(int i=0;i<k;++i)
{
if(i==0)add(S,i,suan(k));
else add(S,i,suan(i));
add(i,(i+1)%k,1);
add(i,(i*10)%k,0);
}
DIJ();
cout<<dis[0];
return 0;
}

浙公网安备 33010602011771号