noip模拟20
A. 玩具
这个题目告诉我们,有些\(dp\)的设计并不是很切合题目,但是却可以进行转移并得到正确的答案.
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define re register ll
#define lf double
#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 void read(ll &ss)
{
ss=0; bool cit=0; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=1;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
if(cit) ss=-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll N=210;
ll m,n,mod;
ll inv[N];
ll dp[N][N],f[N][N],g[N][N];
inline ll ksm(ll a,ll b,ll c)
{
ll temp=1; a=a%c;
while(b)
{
if(b&1) temp=(temp*a)%c;
a=(a*a)%c; b>>=1;
}
return temp%c;
}
signed main()
{
read(n); read(mod);
for(re i=1;i<=n;i++)
inv[i]=ksm(i,mod-2,mod);
dp[1][1]=1;
for(re i=2;i<=n;i++)
{
for(re j=1;j<=i;j++)
{
dp[i][j]=(dp[i-1][j-1]*(j-1)%mod*inv[i]%mod+dp[i-1][j]*(i-j)%mod*inv[i]%mod)%mod;
}
}
g[0][0]=1;
for(re i=0;i<=n;i++)
{
g[0][i]=1;
for(re j=0;j<=n;j++)
{
if(j) f[i][j]=g[i-1][j-1];
if(i<=j){ g[i][j]=1; continue; }
for(re k=0;k<=i;k++)
{
g[i][j]=(g[i][j]+f[k][j]*g[i-k][j]%mod*dp[i][k]%mod)%mod;
}
}
}
ll ans=0;
for(ll i=2;i<=n;i++)
{
ans=(ans+(f[n][i]-f[n][i-1]+mod*1000000)%mod*i%mod)%mod;
// cout<<(f[n][i]-f[n][i-1]+mod*100)*i%mod<<endl;
}
printf("%lld",ans-1);
}
B. y
第一次接触\(meet\ in\ the\ middle\)优化指数.
学会之后这道题看起来便不是那么遥不可及了..
枚举断点然后转移就行了.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll int
#define re register ll
#define lf double
#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 void read(ll &ss)
{
ss=0; bool cit=0; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=1;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
if(cit) ss=-ss;
}
inline void write(ll ss)
{
static int stas[35]; int topps=0;
if(ss<0) putchar('-'),ss=-ss;
do{stas[++topps]=ss%10,ss/=10;}while(ss);
while(topps) putchar(stas[topps--]+48); puts("");
}
} using namespace BSS;
const ll N=100;
ll n,m,d,ts,cnt;
ll head[N];
ll vis[(1<<22)+50];
ll f[N][N][(1<<12)+50];
struct I { ll u,v,w,nxt; } e[N*N*2];
vector<ll> vec[N],vec2[N];
inline void add(ll u,ll v,ll w)
{
e[++ts].u=u;
e[ts].v=v;
e[ts].w=w;
e[ts].nxt=head[u];
head[u]=ts;
}
signed main()
{
read(n); read(m); read(d);
ll u,v,w;
for(ll i=1;i<=m;i++)
{
read(u); read(v); read(w);
add(u,v,w); add(v,u,w);
}
f[0][1][0]=1;
for(ll i=0;i<(d+1)/2;i++)
{
for(ll j=1;j<=n;j++)
{
for(ll k=head[j];k;k=e[k].nxt)
{
for(ll s=0;s<=(1<<((d+1)/2))-1;s++)
{
f[i+1][e[k].v][(s<<1)|e[k].w]|=f[i][j][s];
}
}
}
}
for(ll i=1;i<=n;i++)
{
for(ll s=0;s<(1<<(d+1)/2);s++)
{
if(f[(d+1)/2][i][s])
{
vec[i].push_back(s);
}
}
}
memset(f,0,sizeof f);
for(ll i=1;i<=n;i++) f[0][i][0]=1;
for(ll i=0;i<d-(d+1)/2;i++)
{
for(ll j=1;j<=n;j++)
{
for(ll k=head[j];k;k=e[k].nxt)
{
for(ll s=0;s<=(1<<(d-(d+1)/2))-1;s++)
f[i+1][e[k].v][(s<<1)|e[k].w]|=f[i][j][s];
}
}
}
for(ll i=1;i<=n;i++)
{
for(ll s=0;s<=(1<<(d-(d+1)/2))-1;s++)
{
if(f[d-(d+1)/2][i][s])
{
vec2[i].push_back(s);
}
}
}
for(ll i=1;i<=n;i++)
{
for(ll j=0;j<vec[i].size();j++)
{
for(ll k=0;k<vec2[i].size();k++)
{
vis[(vec[i][j]<<(d-(d+1)/2))|vec2[i][k]]=1;
}
}
}
ll ans=0;
for(ll i=0;i<=(1<<d)-1;i++) ans+=vis[i];
write(ans);
return 0;
}
C. z
让我充分认知了\(C++11\ STL\)的强大.
如果存在一个 \(x_i\) 使得 \(x_i−1 = x_i\) 或 \(x_i−1 < x_i < x_i+1\) 或 \(x_i−1 > x_i > x_i+1\),那么可以删去它。
需要行走的路径可以表示为一正一负的位移,在 l 不超过最小的位移绝对值时,答案是一个
一次函数。
当 l 超过这个值时,意味着当我们到达一个位置时,后继位置直接被接触到,我们需要将 3
个位移合并为 1 个(当然在首尾位置时可能要特判),答案又是一个一次函数。
用 \(map/\)链表维护位移序列,\(priority\ queue\) 维护最小位移绝对值,离线询问即可.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define p() printf("Pass")
#define ll long long int
#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);
#define pass() printf("Pass")
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+50;
ll n,m,tot;
ll poss[N],pos[N],pre[N],nxt[N],ans[N];
map<ll,ll> maps;
struct I
{
ll len,id;
bool operator <(const I &i) const
{
return len==i.len ? id<i.id : len<i.len ;
}
bool operator >(const I &i) const
{
return len==i.len ? id>i.id : len>i.len ;
}
} p[N];
inline ll getans(ll x)
{
if(maps.size() and maps.begin()->second<0) return tot-(maps.size()-1)*x;
else return tot-maps.size()*x;
}
inline void Work()
{
priority_queue<I,vector<I>,greater<I> > que;
ll tem,tmp,id,t=1; I temp;
for(ll i=1;i<=n;i++)
{
tmp=pos[i]-pos[i-1];
maps[i]=tmp; tot+=abs(tmp);
que.push((I){abs(tmp),i});
}
map<ll,ll>::iterator it;
while(que.size())
{
temp=que.top(); id=temp.id; tmp=temp.len;
que.pop(); it=maps.find(id);
if(it==maps.end() or abs(it->second)!=tmp) continue;
while(t<=m and tmp>p[t].len)
ans[p[t].id]=getans(p[t].len),t++;
if(it==maps.begin())
{
if(maps[id]<=0) continue;
if(it==prev(maps.end()))
{
maps.erase(it);
tot-=tmp;
}
else
{
tmp=next(it)->second;
maps.erase(next(it));
tot+=-abs(tmp)-abs(it->second);
if(tmp+(it->second))
{
maps[id]=tmp+(it->second); tot+=abs(maps[id]);
que.push((I){abs(maps[id]),id});
}
else maps.erase(it);
}
}
else
{
if(it==prev(maps.end()))
{
maps.erase(it);
tot-=tmp;
}
else
{
tmp=next(it)->second+it->second+prev(it)->second;
tot+=abs(tmp)-abs(next(it)->second)-abs(it->second)-abs(prev(it)->second);
maps.erase(next(it)); maps.erase(prev(it));
maps[it->first]=tmp;
que.push((I){abs(tmp),it->first});
}
}
}
while(t<=m)
ans[p[t].id]=getans(p[t].len),t++;
return ;
}
signed main()
{
n=read(); m=read();
for(ll i=1;i<=n;i++) poss[i]=read();
ll temp=0;
for(re i=1;i<=n;i++)
if(poss[i]!=poss[i-1]) pos[++temp]=poss[i];
Fill(poss,0); Copy(poss,pos); Fill(pos,0);
n=temp,temp=0;
for(re i=1;i<=n-1;i++)
{
if(poss[i]>pos[temp] and poss[i]<poss[i+1]) continue;
if(poss[i]<pos[temp] and poss[i]>poss[i+1]) continue;
pos[++temp]=poss[i];
}
pos[++temp]=poss[n];
n=temp;
for(re i=1;i<=m;i++) p[i].len=read(),p[i].id=i;
sort(p+1,p+1+m);
Work();
for(re i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
// 高超的删减技术:除去无用的、合并冗余的..