NOIP2014题解
Day1
生活大爆炸版石头剪刀布 rps
简单模拟题,注意细节
#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int ans[5][5]={0,-1,1,1,-1,1,0,-1,1,-1,-1,1,0,-1,1,-1,-1,1,0,1,1,1,-1,-1,0};
int n,na,nb,a[500],b[500],A,B;
int main()
{
n=read();na=read();nb=read();
for(int i=0;i<na;++i)a[i]=read();
for(int i=0;i<nb;++i)b[i]=read();
for(int i=0;i<n;++i)
{
int d=ans[a[i%na]][b[i%nb]];
if(d==1)A+=1;if(d==-1)B+=1;
}
printf("%d %d\n",A,B);
return 0;
}
联合权值 link
可以说非常简单了,先算出每个点周围点的权值和,在计算它们的平方和。
答案就是权值和的平方减去平方和。
#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 10007
#define MAX 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,W[MAX],ans,S[MAX],SS[MAX];
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
for(int i=1;i<=n;++i)W[i]=read();
for(int u=1;u<=n;++u)
{
int mx=0,mxx=0;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;S[u]=(S[u]+W[v])%MOD;SS[u]=(SS[u]+W[v]*W[v])%MOD;
if(W[v]>mx)mxx=mx,mx=W[v];
else if(W[v]>mxx)mxx=W[v];
}
ans=max(ans,mx*mxx);
}
printf("%d ",ans);
ans=0;
for(int i=1;i<=n;++i)ans=(ans+S[i]*S[i])%MOD;
for(int i=1;i<=n;++i)ans=(ans+MOD-SS[i])%MOD;
printf("%d\n",ans);
return 0;
}
飞扬的小鸟 bird
不错的\(dp\)题。
设\(f[i][j]\)表示到达\((i,j)\)位置的最小步数。
转移很显然,类似背包可以不用枚举向上飞的次数。
注意先转移向上飞,因为你至少要飞一次,所以先从\(i-1\)转移飞一次,再在\(i\)行内背包转移。
转移完之后再转移下降的情况。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 10010
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,m,K,L[MAX],H[MAX],X[MAX],Y[MAX];
int f[MAX][1010],inf;
void cmin(int &x,int y){if(x>y)x=y;}
int main()
{
n=read();m=read();K=read();
for(int i=1;i<=n;++i)X[i]=read(),Y[i]=read();
for(int i=1;i<=n;++i)L[i]=0,H[i]=m+1;
for(int i=1;i<=K;++i)
{
int p=read();
L[p]=read();H[p]=read();
}
memset(f,63,sizeof(f));inf=f[0][0];
for(int i=1;i<=m;++i)f[0][i]=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
cmin(f[i][min(j+X[i],m)],f[i-1][j]+1);
for(int j=1;j<=m;++j)
cmin(f[i][min(j+X[i],m)],f[i][j]+1);
for(int j=1;j<=m-Y[i];++j)
cmin(f[i][j],f[i-1][j+Y[i]]);
for(int j=1;j<=L[i];++j)f[i][j]=inf;
for(int j=H[i];j<=m;++j)f[i][j]=inf;
}
int mn=inf;
for(int j=1;j<=m;++j)
if(f[n][j]<1e9)
mn=min(mn,f[n][j]);
if(mn>1e9)
{
for(int i=n-1;i;--i)
for(int j=1;j<=m;++j)
if(f[i][j]<1e9)
{
int sum=0;puts("0");
for(int k=1;k<=i;++k)
if(H[k]<=m)++sum;
printf("%d\n",sum);
return 0;
}
}
else printf("1\n%d\n",mn);
return 0;
}
Day2
无线网络发射器选址 wireless
暴力
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 150
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int s[MAX][MAX];
int d,n,ans=0,way=0;
int main()
{
d=read();n=read();
for(int i=1;i<=n;++i)
{
int x=read(),y=read(),k=read();
s[x][y]+=k;
}
for(int i=0;i<=128;++i)
for(int j=0;j<=128;++j)
{
int ss=0;
for(int k=max(0,i-d);k<=128&&k<=i+d;++k)
for(int l=max(0,j-d);l<=128&&l<=j+d;++l)
ss+=s[k][l];
if(ans<ss)ans=ss,way=1;
else if(ans==ss)way+=1;
}
printf("%d %d\n",way,ans);
return 0;
}
寻找道路 road
沿着反边\(dfs\)一遍找到所有合法点,再\(bfs\)一遍求答案。实际上只需要存反边就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define MAX 10100
#define MAXL 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,m,S,T;
struct Line{int v,next;}e[MAXL<<1];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
bool vis[MAX],book[MAX];
void dfs(int u)
{
if(vis[u])return;vis[u]=true;
for(int i=h[u];i;i=e[i].next)
if(!(i&1))dfs(e[i].v);
}
int dis[MAX];
void bfs()
{
memset(dis,63,sizeof(dis));
queue<int> Q;Q.push(S);dis[S]=0;
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=h[u];i;i=e[i].next)
if((i&1)&&dis[u]+1<dis[e[i].v])
{
if(!book[e[i].v])continue;
dis[e[i].v]=dis[u]+1;
Q.push(e[i].v);
}
}
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
S=read(),T=read();
dfs(T);
for(int u=1;u<=n;++u)
{
bool fl=true;
for(int i=h[u];i;i=e[i].next)
if((i&1)&&!vis[e[i].v]){fl=false;break;}
book[u]=fl;
}
if(!book[S]){puts("-1");return 0;}
bfs();printf("%d\n",dis[T]);
return 0;
}
解方程 equation
显然没法直接算,所以我们取个模。显然一个模数很假,所以我们多搞几个模数。
显然\(x\)大于模数就不用重复算,所以我们对于每个模数预处理。
然后枚举就做完了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int p[]={10007,10013,10017,10023,10029,10037,10097};
int a[7][105];
char ch[10100];
int n,m;
void get(int id)
{
int l=strlen(ch+1);
for(int i=0;i<7;++i)
{
int x=0,fr=1;bool fl=false;
if(ch[1]=='-')fl=true,fr=2;
for(int j=fr;j<=l;++j)
x=(x*10+ch[j]-48)%p[i];
if(fl)x=(p[i]-x)%p[i];
a[i][id]=x;
}
}
int Calc(int id,int x)
{
int ret=0;
for(int i=0,X=1;i<=n;++i,X=X*x%p[id])
ret=(ret+X*a[id][i])%p[id];
return ret;
}
bool vis[7][20000];
int S[1000100],top;
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;++i)scanf("%s",ch+1),get(i);
for(int i=0;i<7;++i)
for(int j=0;j<p[i];++j)
if(Calc(i,j)==0)vis[i][j]=true;
for(int i=1;i<=m;++i)
{
bool fl=true;
for(int j=0;j<7;++j)if(!vis[j][i%p[j]])fl=false;
if(fl)S[++top]=i;
}
printf("%d\n",top);
for(int i=1;i<=top;++i)printf("%d\n",S[i]);
return 0;
}