【题解】NOIP2016 提高组 简要题解
【题解】NOIP2016 提高组 简要题解
玩具迷题(送分)
用异或实现
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e5+5;
int n,m;
string data[maxn];
bool f[maxn];
int main(){
n=qr(); m=qr();
for(int t=0;t<n;++t)
f[t]=qr(),cin>>data[t];
int now=0;
for(int t=1,t1,t2;t<=m;++t){
t1=qr(); t2=qr();
if(t1^f[now]) now=(now+t2)%n;
else now=(now-t2+n)%n;
}
cout<<data[now]<<endl;
return 0;
}
天天爱跑步(树上差分)
树上路径问题常见套路
将一个人从起点到终点的路径上从\(0\)开始编号,可以发现这个路径被分为三段:
- 编号随着深度递减而递增。
- LCA
- 编号随着深度递增而递增。
分别维护两个\(d[u]+-w[u]\)的桶即可。
但是还没有保证只有路径上的点才可能看到这个跑步者,所以直接差分一下即可。
注意到此类路径影响点并且要差分的问题一定要单独处理LCA
瓶颈在\(LCA\),换成\(tarjan\)就\(O(n)\)了
//@winlere
#include<tr1/unordered_map>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std; using namespace std::tr1; typedef long long ll;
typedef vector<int>::iterator Vint;
typedef unordered_map<int,int>::iterator Uint;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=3e5+1;
struct E{int k,val;};
vector<E> q1[maxn],q2[maxn];
typedef vector<E>::iterator Pint;
vector<int> e[maxn];
inline void add(const int&fr,const int&to){
e[fr].push_back(to);
e[to].push_back(fr);
}
int n,m,w[maxn],d[maxn],top[maxn],siz[maxn],son[maxn],r[maxn],ans[maxn],cnt[maxn];
namespace pre{
void dfs(const int&now,const int&last){
d[now]=d[r[now]=last]+1;
siz[now]=1;
for(Vint t=e[now].begin();t!=e[now].end();++t)
if(*t^last) dfs(*t,now),son[now]=siz[*t]>siz[son[now]]?*t:son[now],siz[now]+=siz[*t];
}
void dfs2(const int&now,const int&last){
top[now]=last;
if(son[now])dfs2(son[now],last);
for(Vint t=e[now].begin();t!=e[now].end();++t)
if((*t^r[now])&&(*t^son[now]))
dfs2(*t,*t);
}
inline int lca(int u,int v){
for(;top[u]^top[v];u=r[top[u]])
if(d[top[u]]<d[top[v]]) swap(u,v);
return d[u]>d[v]?v:u;
}
inline void init(){dfs(1,0); dfs2(1,1);}
}
struct D{
unordered_map<int,int> s;
D(){s.clear();}
inline void upd(const int&pos,const int&tag){s[pos]+=tag;}
inline int que(const int&pos){Uint g=s.find(pos);return g==s.end()?0:g->second;}
}s1,s2;
void solve(const int&now,const int&last){
ans[now]-=s1.que(d[now]+w[now])+s2.que(d[now]-w[now]);
for(Vint t=e[now].begin();t!=e[now].end();++t) if(*t^last) solve(*t,now);
for(Pint t=q1[now].begin();t!=q1[now].end();++t) s1.upd(t->k,t->val);
for(Pint t=q2[now].begin();t!=q2[now].end();++t) s2.upd(t->k,t->val);
ans[now]+=s1.que(d[now]+w[now])+s2.que(d[now]-w[now]);
}
int main(){
n=qr(); m=qr();
for(int t=1;t<n;++t) add(qr(),qr());
for(int t=1;t<=n;++t) w[t]=qr();
pre::init();
for(int t=1,t1,t2;t<=m;++t){
t1=qr(),t2=qr();
int g=pre::lca(t1,t2);
int dis=d[t1]+d[t2]-d[g]-d[g];
if(d[t1]-d[g]==w[g]) ++ans[g];
q1[t1].push_back((E){d[t1],1}); q1[g].push_back((E){d[t1],-1});
q2[t2].push_back((E){d[t2]-dis,1}); q2[g].push_back((E){d[t2]-dis,-1});
}
solve(1,0);
for(int t=1;t<=n;++t) printf("%d ",ans[t]);
putchar('\n');
return 0;
}
换教室(期望DP)
注意这是期望,所以要计算失败的贡献。
//@winlere
#include<bits/stdc++.h>
#define int long long
using namespace std; typedef long long ll;
template < class ccf > inline ccf qr(ccf ret){ ret=0;
register char c=getchar();
while(not isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return ret;
}inline int qr(){return qr(1);}
const int maxn=25;
const ll mod=1e9+7;
inline ll Pow(ll base,ll p){
base%=mod;
register ll ret=1;
for(;p;p>>=1,base=base*base%mod)
if(p&1) ret=ret*base%mod;
return ret;
}
ll data[maxn],s,ans,inv[maxn]={1},jie[maxn]={1};
int n;
inline ll C(const ll&n,const ll&m){
if(n<m||m<0||n<0)return 0;
if(n==m)return 1;
register ll ret=inv[m];
for(register ll t=n;t>=n-m+1ll;--t)
ret=t%mod*ret%mod;
return ret;
}
#undef int
int main(){
#define int long long
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
//freopen("out.out","w",stdout);
#endif
for(register int t=1;t<maxn;++t)
inv[t]=inv[t-1]*Pow(t,mod-2ll)%mod;
n=qr();s=qr(1ll);ans=C(s+n-1ll,n-1ll);
for(register int t=1;t<=n;++t)
data[t]=qr(1ll);
for(register int t=1,edd=1<<n,cnt=0;t<edd;++t){
ll f=cnt=0,delt;
for(register int i=1;i<=n;++i)
if(t<<1>>i&1)
f+=data[i]+1ll,++cnt;
delt=C(s-f+n-1ll,n-1ll);
if(cnt&1) ans=(ans-delt)%mod,ans=ans<0?ans+mod:ans;
else ans=(ans+delt)%mod;
}
cout<<ans<<endl;
return 0;
}
组合数问题(二维前缀和)
\(O(n^2)\)处理组合数然后前缀和即可。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define RP(t,a,b) for(int t=(a),edd=(b);t<=edd;t++)
typedef long long ll;
inline ll qr(void){
char c=getchar();
int x=0,q=1;
while(c<48||c>57)
q=c==45?-1:q,c=getchar();
while(c>=48&&c<=57)
x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*q;
}
const int maxn=2002;
ll T,k;
int l,r;
int c[maxn][maxn]={1},ans[maxn][maxn];
int main(){
T=qr();k=qr();
c[1][0]++;
c[1][1]++;
RP(t,2,2000){
c[t][0]=1;
RP(i,1,t){
c[t][i]=(c[t-1][i-1]+c[t-1][i])%k;
ans[t][i]=ans[t-1][i]+ans[t][i-1]-ans[t-1][i-1];
if(!c[t][i]) ans[t][i]++;
}
ans[t][t+1]=ans[t][t];
}
while(T--){
l=qr();
r=qr();
if(r>l) r=l;
cout<<ans[l][r]<<endl;
}
return 0;
}
蚯蚓(队列)
堆只能获得75分(不知道fib和配对堆能不能更高)。受到一堆题的启发,考虑一下用队列维护这个堆。
处理每秒的增量直接记录一个变量即可,注意到当元素push进去时要先减去当前总增量,下次取出时才能计算出真正的增量。
可以发现,后面生成的蚯蚓一定比先前生成的蚯蚓要短。
但是生成的两种蚯蚓之间没有大小的保证。
所以维护三个队列分别表示:原蚯蚓,\(u \times p\)的蚯蚓,\(u-u\times p\)的蚯蚓。每次从三个队列队首取大的那个。
复杂度\(O(n)\) 居然可以过CCF老年机?
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define int long long
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=1e5+1;
queue<int> q[4];
int data[maxn];
int n,m,Q,u,v,T;
double f;
inline int getBig(){
int g=-1e18,ret=-1;
for(int t=1;t<=3;++t)
if(q[t].size()&&q[t].front()>g)
g=q[t].front(),ret=t;
return ret;
}
signed main(){
n=qr(); m=qr(); Q=qr(); u=qr(); v=qr(); T=qr();
f=(double)u/v;
for(int t=1;t<=n;++t) data[t]=qr();
sort(data+1,data+n+1);
for(int t=n;t;--t) q[1].push(data[t]);
for(int t=0,g=T;t<m;++t){
int l=getBig();
int now=q[l].front()+t*Q;
int f1=now*f,f2=now-f1;
q[l].pop(); q[2].push(f1-(t+1)*Q); q[3].push(f2-(t+1)*Q);
if(--g==0) g=T,printf("%lld ",now);
}
putchar('\n');
for(int t=1,g=T;t<=m+n;++t){
int l=getBig();
int now=q[l].front()+m*Q;
if(--g==0) g=T,printf("%lld ",now);
q[l].pop();
}
putchar('\n');
return 0;
}
愤怒的小鸟(搜索)
剪枝然后随机化,注意到枚举一次抛物线后,可能打中很多鸟,那些鸟就不必再和当前鸟枚举抛物线了。
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
#define pf(x) ((x)*(x))
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=21;
int n,W1;
typedef pair<long double,long double> Pt;
const long double eps=1e-10;
Pt data[maxn];
bool hit[maxn];int ans;
struct axx{
long double a,b;
axx(){a=b=0;}
axx(const long double&x,const long double&y){a=x,b=y;}
inline bool operator*(const Pt&s){return abs((a*s.first+b)*s.first-s.second)<=eps;}
inline bool operator ==(const axx&s){return abs(a-s.a)<=eps&&abs(b-s.b)<=eps;}
inline void print(){
printf("{%Lfx*x+%Lfx}\n",a,b);
}
}Err(1,1);
inline axx gen(const Pt&a,const Pt&b){
long double mu=a.x*pf(b.x)-pf(a.x)*b.x;
if(-eps<=mu&&mu<=eps) return Err;
long double z1=b.y*a.x-a.y*b.x;
long double z2=b.y*pf(a.x)-a.y*pf(b.x);
axx ret(z1/mu,-z2/mu);
if(ret.a>=-eps) return Err;
return ret;
}
void dfs(const int&now,const int&s,const int&tol){
if(ans<=s) return;
if(tol==n) return ans=s,void();
if(now>n) return;
if(hit[now]) return dfs(now+1,s,tol);
bool f=0,temp[maxn];
for(int t=1;t<=n;++t) temp[t]=hit[t];
unsigned short sav[maxn];
sav[0]=0;
for(int t=now+1;t<=n;++t)
if(!temp[t]){
axx Cur=gen(data[now],data[t]);
if(Cur==Err) continue;
for(int i=now;i<=n;++i)
if((!hit[i])&&(Cur*data[i]))
sav[++*sav]=i,hit[i]=1,temp[i]=1;
dfs(now+1,s+1,tol+*sav);
for(int i=1;i<=*sav;++i) hit[sav[i]]=0;
sav[0]=0;
f=1;
}
if(!f) hit[now]=1,dfs(now+1,s+1,tol+1),hit[now]=0;
}
int main(){
srand(time(NULL));
int T=qr();
while(T--){
n=qr(); W1=qr();
for(int t=1;t<=n;++t) scanf("%Lf%Lf",&data[t].x,&data[t].y);
stable_sort(data+1,data+n+1);
n=unique(data+1,data+n+1)-data-1;
random_shuffle(data+1,data+n+1);
ans=n;
dfs(1,0,0);
printf("%d\n",ans);
}
return 0;
}
博客保留所有权利,谢绝学步园、码迷等不在文首明显处显著标明转载来源的任何个人或组织进行转载!其他文明转载授权且欢迎!