noip模拟35
A. 玩游戏
可以简单地把这个序列拆成一个 \(2\) ~ \(k\) 的序列\(A\)和 \(k+1\) ~ \(n\) 的序列\(B\).
发现只从\(A\)和\(B\)的开头进行贪心的想法是显然错误的,于是考虑如何改进.
发现当序列\(A\)向前移动时,如果移动之后总和大于 \(0\) ,那么可以选择将\(B\)移动到开头到目前位置的最小值.
因为不会写\(O(nlogn)\),所以糊了一个\(O(n\)~\(n^2)\)的做法,这个复杂度和值域有关,如果卡的很紧的话是可以卡成\(n^2\)的.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define ull unsigned ll
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=1e5+21;
ll m,n,Ts;
ll a[N],b[N];
inline void Pre(){
ll t=read(),s=read(); n=0,m=0; read();
for(re i=2;i<=s;i++) a[s-i+1]=read();
for(re i=2;i<=s;i++) n++,a[n]+=a[n-1];
for(re i=s+1;i<=t;i++) m++,b[m]=b[m-1]+read();
}
inline void Work(){
ll i=0,j=0;
while(i<n){
while(j<m and a[i]+b[j+1]<=0) j++;
i++;
while(j>0 and a[i]+b[j]>0) j--;
if(a[i]+b[j]>0) { puts("No"); return ; }
}
while(j<m and a[i]+b[j+1]<=0) j++;
if(i<n or j<m) puts("No");
else puts("Yes");
}
signed main(){
Ts=read();
while(Ts--){
Pre(),Work();
}
exit(0);
}
B. 排列
这种题目给的信息很少,那么一定就是推理性质了.
一个美妙的\(dp\)定义,\(f_{i,j,0/1}\)表示长度为\(i\)的序列,至多需要\(j\)次完成消除的方案,其中\(0/1\)表示边界有没有挨着更大值.
这里的\(0/1\)可以通俗地理解为:如果是\(0\),那么就是边界挨着更小值或者边界没有挨着值;反之则为\(1\).
于是\(dp\)转移就可以很显然地出现了.
\(f_{i,j,0}=\Sigma C^{k−1}_{i−1}∗f_{k−1,j−1,1}∗f_{i−k,j,0}\)
\(f_{i,j,1}=\Sigma C^{k−1}_{i−1}∗(f_{k−1,j,1}∗f_{i−k,j−1,1}+f_{k−1,j−1,1}∗f_{i−k,j,1}−f_{k−1,j−1,1}∗f_{i−k,j−1,1})\)
统计答案时差分一下就出来了.
\(ans=\Sigma C^{i−1}_{n−1}∗(f_{i−1,k,0}∗f_{n−i,k,0}−f_{i−1,k−1,0}∗f_{n−i,k−1,0})\)
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define lf double
#define re register ll
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=1e3+21;
ll n,m,mod,ans;
ll C[N][N];
ll f[N][N][2];
signed main(){
n=read(),m=read(),mod=read(),C[0][0]=1;
for(re i=1;i<=n;i++){
C[0][i]=1;
for(re j=1;j<=i;j++) C[j][i]=(C[j][i-1]+C[j-1][i-1])%mod;
}
if(m-1>=ceil(log2(n))) puts("0"),exit(0);
for(re i=0;i<=m;i++) f[0][i][0]=1,f[0][i][1]=1;
for(re i=1;i<=n;i++)
for(re j=1;j<=m;j++)
for(re k=1;k<=i;k++){
(f[i][j][0]+=C[k-1][i-1]*f[k-1][j-1][1]%mod*f[i-k][j][0]%mod)%=mod,
(f[i][j][1]+=C[k-1][i-1]*((f[k-1][j][1]*f[i-k][j-1][1]%mod+f[k-1][j-1][1]*f[i-k][j][1]%mod-f[k-1][j-1][1]*f[i-k][j-1][1]%mod+mod)%mod)%mod)%=mod;
}
for(re i=1;i<=n;i++) (ans+=C[i-1][n-1]*((f[i-1][m][0]*f[n-i][m][0]%mod-f[i-1][m-1][0]*f[n-i][m-1][0]%mod+mod)%mod)%mod)%=mod;
printf("%lld\n",ans);
exit(0);
}
C. 最短路
思路来自 \(Yubai\) .
发现需要从 \(1\) 走到 \(n\) ,再从 \(n\) 走到 \(1\) .
由于原图是有向边,所以可以把题目中给出的边看成正边,把正边的 \(u\) 和 \(v\) 倒过来看成反边.
于是可以把原题转换成从 \(1\) 走到 \(n\),需要一次只经过正边的路径和一次只经过反边的路径.
设 \(dis_{i,j}\) 表示走正边走到 \(i\),再走反边走到 \(j\).
然后跑一个二维最短路就可以了.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define lf double
#define re register ll
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=257,M=257*257;
ll m,n,lmt;
ll fee[N];
ll dis[N][N],vis[N][N];
struct Graph{
ll ts; ll head[N];
struct I { ll u,v,nxt; } e[M];
inline void add(ll u,ll v){
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u],
head[u]=ts;
}
}A,B;
struct II {
ll u,v,w;
bool operator <(const II &i)const{
return i.w<w;
}
};
priority_queue<II> que;
bitset<N> bit[N][N];
inline void Dijk(){
Fill(dis,0x3f); ll u,v,w,temp;
dis[1][1]=fee[1],que.push(II{1,1,fee[1]});
while(que.size()){
u=que.top().u,v=que.top().v,w=que.top().w,que.pop();
if(vis[u][v]) continue; vis[u][v]=1;
for(re i=A.head[u];i;i=A.e[i].nxt){
(temp=0)+=fee[A.e[i].v]*(!bit[u][v][A.e[i].v]);
if(dis[A.e[i].v][v]>dis[u][v]+temp){
bit[A.e[i].v][v]=bit[u][v],bit[A.e[i].v][v][A.e[i].v]=1;
dis[A.e[i].v][v]=dis[u][v]+temp;
que.push(II{A.e[i].v,v,dis[A.e[i].v][v]});
}
}
for(re i=B.head[v];i;i=B.e[i].nxt){
(temp=0)+=fee[B.e[i].v]*(!bit[u][v][B.e[i].v]);
if(dis[u][B.e[i].v]>dis[u][v]+temp){
bit[u][B.e[i].v]=bit[u][v],bit[u][B.e[i].v][B.e[i].v]=1;
dis[u][B.e[i].v]=dis[u][v]+temp;
que.push(II{u,B.e[i].v,dis[u][B.e[i].v]});
}
}
}
}
signed main(){
n=read(),m=read(); ll u,v;
for(re i=1;i<=n;i++) fee[i]=read();
for(re i=1;i<=m;i++) u=read(),v=read(),A.add(u,v),B.add(v,u);
Dijk();
if(dis[n][n]>=0x3f3f3f3f) puts("-1"),exit(0);
printf("%lld\n",dis[n][n]);
exit(0);
}
D. 矩形
发现很多矩形由于被包含,所以是无用的.
在没有思路的时候,或许删减一下是一个挺有效的策略,比如曾经做过的一道题目 \(z\).
关于如何删减,可以发现是一个显然的三维偏序.
然后考虑删减完之后应该怎么做,手玩几个样例,发现所有位于同一联通块的矩形有两条边是交叉形成 '十' 字的.
于是线段树维护即可.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll int
#define ull unsigned ll
#define lf double
#define lbt(x) (x&(-x))
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read() {
ll res=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return cit?res:-res;
}
} using namespace BSS;
const ll N=1e5+21;
ll m,n,cnt,tot,ans;
ll lsh[N<<2],vis[N];
multiset<pair<ll,ll> > s[N<<2];
struct I { ll x0,y0,x2,y2,die; } p[N];
vector<I> vec;
inline bool cmp1(I i,I j){
if(i.x0!=j.x0) return i.x0<j.x0;
if(i.y0!=j.y0) return i.y0<j.y0;
if(i.x2!=j.x2) return i.x2>j.x2;
return i.y2>j.y2;
}
inline bool cmp2(I i,I j){
if(i.y0!=j.y0) return i.y0<j.y0;
if(i.x2!=j.x2) return i.x2>j.x2;
return i.y2>j.y2;
}
struct Bit_Array{
ll res; ll c[N<<2];
inline void update(ll x,ll w){
for(;x>0;x&=(x-1)) c[x]=max(c[x],w);
}
inline void del(ll x){
for(;x>0;x&=(x-1)) c[x]=0;
}
inline ll query(ll x){
res=0; for(;x<=cnt;x+=lbt(x)) res=max(c[x],res); return res;
}
} bit;
void cdq(ll l,ll r){
if(l==r) return ;
// if(l==1 and r==n) { for(int i=1;i<=n;i++) cout<<p[i].x0<<' '<<p[i].y0<<" "<<p[i].x2<<' '<<p[i].y2<<endl; puts(""); }
ll mid=(l+r)>>1; cdq(l,mid),cdq(mid+1,r);
sort(p+l,p+mid+1,cmp2),sort(p+mid+1,p+r+1,cmp2);
ll p1=l,p2=mid+1,res;
// cout<<"L:"<<l<<" R:"<<r<<endl;
while(p1<=mid and p2<=r){
if(p[p1].y0<=p[p2].y0){ bit.update(p[p1].x2,p[p1].y2),p1++; }
else{ res=bit.query(p[p2].x2),p[p2].die|=(res>=p[p2].y2),p2++; }
}
while(p2<=r) res=bit.query(p[p2].x2),p[p2].die|=(res>=p[p2].y2),p2++;
for(int i=l;i<=p1;i++) bit.del(p[i].x2);
}
struct Segment{
#define ls (x<<1)
#define rs (x<<1|1)
multiset<pair<ll,ll> > tr[N<<2];
void update(ll x,ll l,ll r,ll ql,ll qr,pair<ll,ll> w){
if(l>=ql and r<=qr) return tr[x].insert(w),void();
ll mid=(l+r)>>1;
if(ql<=mid) update(ls,l,mid,ql,qr,w);
if(qr>mid) update(rs,mid+1,r,ql,qr,w);
}
ll query(ll x,ll l,ll r,ll pos,pair<ll,ll> w){
if(tr[x].size()){
auto it=tr[x].lb(mp(w.first,0));
if(it!=tr[x].end() and it->first<=w.second){
ll res=it->second; tr[x].erase(it);
return res;
}
}
if(l==r) return -1; ll mid=(l+r)>>1;
return pos<=mid ? query(ls,l,mid,pos,w) : query(rs,mid+1,r,pos,w);
}
#undef ls
#undef rs
}X,Y;
void dfs(ll i){
if(vis[i]) return ; vis[i]=1; ll v;
while((v=X.query(1,1,cnt,p[i].x0,mp(p[i].y0,p[i].y2)))!=-1) dfs(v);
while((v=X.query(1,1,cnt,p[i].x2,mp(p[i].y0,p[i].y2)))!=-1) dfs(v);
while((v=Y.query(1,1,cnt,p[i].y0,mp(p[i].x0,p[i].x2)))!=-1) dfs(v);
while((v=Y.query(1,1,cnt,p[i].y2,mp(p[i].x0,p[i].x2)))!=-1) dfs(v);
}
signed main(){
n=read();
for(int i=1;i<=n;i++){
lsh[++cnt]=p[i].x0=read(),lsh[++cnt]=p[i].y0=read(),
lsh[++cnt]=p[i].x2=read(),lsh[++cnt]=p[i].y2=read();
}
sort(lsh+1,lsh+1+cnt),cnt=unique(lsh+1,lsh+1+cnt)-lsh-1;
for(int i=1;i<=n;i++){
p[i].x0=lb(lsh+1,lsh+1+cnt,p[i].x0)-lsh,p[i].y0=lb(lsh+1,lsh+1+cnt,p[i].y0)-lsh;
p[i].x2=lb(lsh+1,lsh+1+cnt,p[i].x2)-lsh,p[i].y2=lb(lsh+1,lsh+1+cnt,p[i].y2)-lsh;
}
sort(p+1,p+1+n,cmp1),cdq(1,n);
for(int i=1;i<=n;i++) if(!p[i].die) vec.push_back(p[i]);
n=0; for(auto i : vec) p[++n]=i;
for(int i=1;i<=n;i++){
X.update(1,1,cnt,p[i].x0,p[i].x2,mp(p[i].y0,i));
X.update(1,1,cnt,p[i].x0,p[i].x2,mp(p[i].y2,i));
Y.update(1,1,cnt,p[i].y0,p[i].y2,mp(p[i].x0,i));
Y.update(1,1,cnt,p[i].y0,p[i].y2,mp(p[i].x2,i));
}
for(int i=1;i<=n;i++){
if(!vis[i]) ans++,dfs(i);
}
printf("%d\n",ans),exit(0);
}