2019年8月1日(基本功练习2)
Posted alanallen21love28
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019年8月1日(基本功练习2)相关的知识,希望对你有一定的参考价值。
时间不够了,\(T2\)没切,好烦,T_T……
prob1:Reign
考虑分割为间隔为\(k\)的两个区间,分别跑最大子段和,然后移动中间的\(k\)区间,左边加入,右边弹出,\(O(n)\)得解(\(md\),最大子段和竟然调了一个多小时,我是要退役了吗):
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define int long long
#define jinitaimei signed
const int xx=1e5+10;
const int inf=1e18;
struct lineint ff,f;le[xx];
int a[xx];
priority_queue<int>q,del,p;
jinitaimei main()
int t;
scanf("%lld",&t);
while(t--)
while(!q.empty()) q.pop();
while(!del.empty()) del.pop();
while(!p.empty()) p.pop();
int n,k;
scanf("%lld%lld",&n,&k);
memset(a,0,sizeof(a));
fur(i,1,n) scanf("%lld",&a[i]);
fur(i,0,n+1) le[i].ff=le[i].f=-inf;
le[1].ff=le[1].f=a[1];p.push(a[1]);
fdr(i,n,2+k)
if(le[i+1].ff>=0) le[i].f=le[i+1].ff+a[i];
else le[i].f=a[i];
if((a[i]<0&&le[i+1].ff+a[i]>=0)||(a[i]>=0&&le[i+1].ff>=0)) le[i].ff=le[i+1].ff+a[i];
else le[i].ff=a[i];
q.push(le[i].f);
int ans=p.top()+q.top();
fur(i,2,n-k-1)
del.push(le[i+k].f);
while(!del.empty()&&!q.empty()&&del.top()==q.top()) del.pop(),q.pop();
if(le[i-1].ff>=0) le[i].f=le[i-1].ff+a[i];
else le[i].f=a[i];
if((le[i-1].ff+a[i]>=0&&a[i]<0)||(a[i]>=0&&le[i-1].ff>=0)) le[i].ff=le[i-1].ff+a[i];
else le[i].ff=a[i];
p.push(le[i].f);
ans=max(ans,p.top()+q.top());
printf("%lld\n",ans);
return 0;
prob6:Chef and Digit Jumps
连边,左右两点直接连,等值节点菊花连,跑最短路即可(这里用的配对堆):
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define nm make_pair
#define ll long long
#define pa pair<ll,int>
#define heep __gnu_pbds::priority_queue<pa,greater<pa> >
inline int read()
int x=0,f=1;char ch=getchar();
for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
const int xx=1e5+20;
int dis[xx],n;
char ch[xx];
vector<pair<int,int> >e[xx];
heep q;
heep::point_iterator id[xx];
const int inf=1e7;
inline void dij()
fur(i,2,n+11) dis[i]=inf;
id[1]=q.push(nm(0,1));
while(!q.empty())
int u=q.top().second;q.pop();
fur(i,0,(int)e[u].size()-1)
if(e[u][i].second+dis[u]<dis[e[u][i].first])
dis[e[u][i].first]=dis[u]+e[u][i].second;
if(id[e[u][i].first]!=0) q.modify(id[e[u][i].first],nm(dis[e[u][i].first],e[u][i].first));
else id[e[u][i].first]=q.push(nm(dis[e[u][i].first],e[u][i].first));
int main()
scanf("%s",ch+1);
n=strlen(ch+1);
fur(i,1,n)
ch[i]=ch[i]-'0'+1;
e[n+(int)ch[i]].push_back(nm(i,0));
e[i].push_back(nm(n+ch[i],1));
if(ch[i-1]!=ch[i]&&i!=1) e[i].push_back(nm(i-1,1));
if(ch[i+1]!=ch[i]&&i!=n) e[i].push_back(nm(i+1,1));
dij();
printf("%d\n",dis[n]);
return 0;
prob3:Strongly Connected City
题目大意:共\(n\)条横向有向道路与\(m\)条纵向有向道路,形成\(n* m\)个路口,问是否任意两个城市间两两可达(\(n,m<=20\))
数据范围小啊,直接暴力搞啊,\(floyed\)大法好啊,长命没烦恼啊:
#include<iostream>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
inline int read()
int x=0,f=1;char ch=getchar();
for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
const int xx=410;
bool dis[xx][xx];
int main()
int n=in,m=in;
char op[21];
scanf("%s",op+1);
fur(i,1,n)
if(op[i]=='>') fur(j,1,m) fur(k,j,m) dis[j+(i-1)*m][k+(i-1)*m]=true;
else fur(j,1,m) fur(k,1,j) dis[j+(i-1)*m][k+(i-1)*m]=true;
scanf("%s",op+1);
fur(i,1,m)
if(op[i]=='v') fur(j,1,n) fur(k,j,n) dis[i+(j-1)*m][i+(k-1)*m]=true;
else fur(j,1,n) fur(k,1,j) dis[i+(j-1)*m][i+(k-1)*m]=true;
fur(k,1,n*m) fur(i,1,n*m) fur(j,1,n*m) if(dis[i][k]&&dis[k][j]) dis[i][j]=true;
fur(i,1,n*m) fur(j,1,n*m) if(!dis[i][j]) puts("NO"),exit(0);
puts("YES");
return 0;
prob2:trips
考虑反向跑,每次都删去度小于\(k\)的点(对点标记),删去与其相连的边(同样对边进行标号标记即可),(这里使用小根配对堆维护度的取出):
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define pa pair<int,int>
#define heep __gnu_pbds::priority_queue<pa,greater<pa> >
#define nm make_pair
inline int read()
int x=0,f=1;char ch=getchar();
for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
const int xx=2e5+10;
int du[xx],cnt=0;
vector<pa>e[xx];
struct edgeint u,v,ans;bool use;d[xx];
heep p;
heep::point_iterator id[xx];
bool cut[xx];
int main()
int n=in,m=in,k=in;
fur(i,1,m)
int x=in,y=in;
d[i].u=x,d[i].v=y;
e[x].push_back(nm(y,i));
e[y].push_back(nm(x,i));
++du[x];++du[y];
fur(i,1,n) id[i]=p.push(nm(du[i],i));
cnt=n;
fdr(i,m,1)
while(!p.empty()&&p.top().first<k)
int u=p.top().second;p.pop();
cut[u]=true;du[u]=0;
--cnt;
fur(j,0,(int)e[u].size()-1)
if(cut[e[u][j].first]) continue;
if(d[e[u][j].second].use) continue;
d[e[u][j].second].use=true;
--du[e[u][j].first];
p.modify(id[e[u][j].first],nm(du[e[u][j].first],e[u][j].first));
d[i].ans=cnt;
if(!d[i].use)
if(cut[d[i].u]||cut[d[i].v]) continue;
--du[d[i].u];
--du[d[i].v];
p.modify(id[d[i].u],nm(du[d[i].u],d[i].u));
p.modify(id[d[i].v],nm(du[d[i].v],d[i].v));
d[i].use=true;
fur(i,1,m) printf("%d\n",d[i].ans);
return 0;
prob4:Reach Equilibrium
小凯的疑惑级的数学。
先看合法的充要条件:选出的\(n\)个向量的模均小于它们的和的一半\(k/2\),设向量模为\(a_i\),则为\(\forall_i \in na_i<k/2\).
这样的概率不好直接求,考虑补集思想,计算不构成的概率,用一减之。
即求\(a_i>=k/2\)的概率。
由于当\(a_i>=k/2\)时,满足\(\forall_j \in n \land j \ne ia_j<k/2\),故\(n\)个模长大于等于\(k/2\)的事件是互相独立的。
\(so,what~we~want~can~be~P(\exists_i \in na_i>=k/2)~which~can~be~expressed~that~n* P(a_i>=k/2)(i \in n)\)
考虑求\(P(a_i>=k/2)\),直接硬掰不美,考虑几何概型。
在长度为\(len\)的线段上取\(n\)条线段其实是在长度为\(len\)的线段上取\(n-1\)个点分割该线段,由于取线段的概率相等,故点落在线段上的任意位置的概率相等。而\(n\)条线段中有一条的模大于等于\(k/2\)的充要条件是这\(n-1\)个点全部落在\(len\)线段的一边。其中一个点落在指定一边的概率为\(\dfrac12\),则\(n-1\)个点全部落在指定一边的概率为\(\dfrac12^n-1\),即\(P(a_i>=k/2)=\dfrac12^n-1\),带入之前得到的公式,则有:
\[P(\exists_i \in na_i>=k/2)=\dfracn2^n-1\]
题目所求为:
\[ans\equiv (1-\dfracn2^n-1)(mod~1e9+7)\]
式子难推,妹子难追,所幸,代码清真:
#include<iostream>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define int long long
#define jinitaimei signed
inline int read()
int x=0,f=1;char ch=getchar();
for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
const int mod=1e9+7;
inline int power(int x,int k)int res=1;for(;k;k>>=1,x=x*x%mod) if(k&1) res=res*x%mod;return res;
jinitaimei main()
int n=in,k=in;
printf("%lld\n",(power(2,n-1)-n+mod)%mod*power(2,(n-1)*(mod-2))%mod);
return 0;
prob5:Partition into Permutations
正解是双向链表,可惜看不懂。像上次双向链表准备摧残我幼小的灵魂时,\(yyz\)大仙用\(yyz\)神仙队列救了我一样,\(wz\)大仙在双向链表的恐怖威压下一波\(DP\)秒切该题,将复杂的代码给压成了\(44\)行的清真代码
\(f_i,j\) 表示当前指向了数字\(i\),并且\(i\)数字保留\(j\)个的最少步数;\(h_i\)表示\(i\)数字在原序列中的出现次数。则状态转移方程为:
\[f_i,j=min(\forall_k>=j \land k<=max(\forall_q \in nh_q)f_i,k)+abs(j-h_i)\]
但是空间开不下,于是考虑可将\(f\)数组降一维。
但输入的\(a_i\)范围很大(\(1e9\)),依旧很大。但仔细思考后,显然有当\(a_i>2*n\)时,删去\(a_i\)必定更优。将\(a_i>2*n\)的数量给计入\(basic\),然后不管即可。
目标:\(min(\forall_i>=0 \land i<=max(\forall_q \in nh_q)f_i)+basic\)(式子中已降维)
同理,游戏结束:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define int long long
inline int read()
int x=0,f=1;char ch=getchar();
for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
return x*f;
const int xx=1e6+10;
int f[xx<<1];
int h[xx<<1];
signed main()
int t=in;
while(t--)
int n=in,basic=0,mx=0,mxx=0;
fur(i,1,n)
int a=in;
if(a<=n*2) mx=max(mx,a),h[a]++;
else basic++;
fur(i,1,mx) mxx=max(mxx,h[i]);
fur(i,0,mxx) f[i]=abs(h[1]-i);
fur(i,2,mx)
fdr(j,mxx-1,0) f[j]=min(f[j],f[j+1]);
fur(j,0,mxx) f[j]+=abs(h[i]-j);
int ans=f[0];
fur(i,1,mxx) ans=min(ans,f[i]);
printf("%lld\n",ans+basic);
fur(i,0,mx) h[i]=0;
return 0;
以上是关于2019年8月1日(基本功练习2)的主要内容,如果未能解决你的问题,请参考以下文章