思路
就是模拟,只是怎样更优雅的模拟而已,不过多点if也没关系,能拿分才是关键嘛。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int read() {
int x=0,f=1;
char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
const int N=210;
int pd[10][10];
void pre() {
pd[0][2]=pd[0][3]=pd[1][3]=pd[2][4]=pd[3][4]=1;
for(int i=0;i<=4;++i)
for(int j=0;j<i;++j)
pd[i][j]=pd[j][i]^1;
}
int a[N],b[N];
int main() {
pre();
int na,nb,n;
scanf("%d%d%d",&n,&na,&nb);
for(int i=0;i<na;++i)
scanf("%d",&a[i]);
for(int i=0;i<nb;++i)
scanf("%d",&b[i]);
int ansa=0,ansb=0,ajs=0,bjs=0;
for(int i=1;i<=n;++i) {
ajs%=na;
bjs%=nb;
ansa+=pd[a[ajs]][b[bjs]];
ansb+=pd[b[bjs]][a[ajs]];
ajs++;
bjs++;
}
printf("%d %d
",ansa,ansb);
return 0;
}
思路
仔细理解题意可以发现,其实题目就是让着对于每个点,将其所能到达的每个点两两相乘,求出所得之和和最大值。如果每个点都n方枚举的话应该是过不了的。所以可以稍微推一下O(n)计算的方法,其实就是一边对前面的孩子求前缀和,一边乘当前的孩子,最后再乘2即可。总复杂度似乎是(O(n^2))的,但是发现因为这是一棵树,所以对于每条边只会计算到两次,实际复杂度是(O(n))的。
最大值不取模,最大值不取模,最大值不取模
代码:
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=200000+1000,mod=10007;
ll read() {
ll x=0,f=1;
char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
struct node
{
int v,nxt;
}e[N*2];
int ejs;
int head[N];
int w[N];
int ans,sum;
void add(int u,int v) {
e[++ejs].v=v;e[ejs].nxt=head[u];head[u]=ejs;
}
int K=0;
void work(int x) {
int kk=0;
int mx=-1,tmx=-1;
for(int i=head[x];i;i=e[i].nxt)
kk+=w[e[i].v];
for(int i=head[x];i;i=e[i].nxt) {
K++;
int v=e[i].v;
sum+=(kk-w[v])*w[v]%mod;
sum%=mod;
if(w[v]>mx) {
tmx=mx;
mx=w[v];
}
else if(w[v]>tmx) tmx=w[v];
}
ans=max(ans,mx*tmx);
}
int main() {
int n=read();
for(int i=1;i<n;++i) {
int x=read(),y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=n;++i) w[i]=read();
for(int i=1;i<=n;++i) work(i);
printf("%d %d
",ans,sum);
return 0;
}
75分思路
用f[i][j]表示到位置i,高度为j所需要花费的最小点击次数。然后每次利用当前可以存在的位置去更新下一个位置即可。复杂度(O(nmk))
100分思路
在上面思路的基础上可以发现,第三层枚举,也就是枚举下一层可能存在的位置的时候,可以不必从当前位置转移,而是从下一位置已经转移了的位置进行转移。比如现在位置是1,每点击一下会上生2,那么下一位置位于5的最小点击次数可以不用f[i][1]+2更新,而是用f[i+1][3]+1,明显f[i+1][3]之前已经被更新过了。需要注意的是,小鸟再升到最高处之后,可以继续点击,只不过不会再升了而已,需要特殊处理一下。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=10100,INF=0x3f3f3f3f;
int f[N][3010],X[N],Y[N],a[N],L[N],H[N];
int n,m,K;
int js,bzz[N];
int read() {
int x=0,f=1;
char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int main() {
n=read(),m=read(),K=read();
memset(f,0x3f,sizeof(f));
for(int i=0;i<n;++i) {
H[i]=m+1;
X[i]=read();
Y[i]=read();
}
H[n]=m+1;
for(int i=1;i<=K;++i) {
int x=read();
bzz[x]=1;
L[x]=read();
H[x]=read();
}
for(int i=1;i<=m;++i)
f[0][i]=0;
for(int i=1;i<=n;++i) {
for(int j=X[i-1]+1;j<=m+X[i-1];++j)
f[i][j]=min(f[i][j-X[i-1]]+1,f[i-1][j-X[i-1]]+1);
for(int j=m+1;j<=m+X[i-1];++j)
f[i][m]=min(f[i][m],f[i][j]);
for(int j=1;j<=m-Y[i-1];++j)
f[i][j]=min(f[i][j],f[i-1][j+Y[i-1]]);
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 bz=0;
for(int j=L[i];j<H[i];++j) {
if(f[i][j]<INF) {
bz=1;
break;
}
}
if(bz==0) {
printf("0
%d
",js);
return 0;
}
if(bzz[i]) js++;
}
int ans=INF;
for(int i=L[n]+1;i<H[n];++i) ans=min(ans,f[n][i]);
printf("1
%d
",ans);
return 0;
}
思路
考虑到数据范围很小,可以枚举每个路口,然后在扫一遍。去看如果将网络发射器安在当前位置会有多少收益,取最大值即可。复杂度(O(n^4))
代码
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll a[200][200];
int d;
int n;
ll work(int x,int y) {
ll ans=0;
for(int i=max(x-d,0);i<=min(x+d,128);++i)
for(int j=max(y-d,0);j<=min(y+d,128);++j)
ans+=a[i][j];
return ans;
}
ll read() {
ll x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) {x=x*10+c-'0';c=getchar();}
return x;
}
ll ans,pans;
int main() {
d=read();n=read();
for(int i=1;i<=n;++i) {
int x=read(),y=read(),z=read();
a[x][y]=z;
}
for(int x=0;x<=128;++x) {
for(int y=0;y<=128;++y) {
ll z=work(x,y);
if(z>ans) pans=1,ans=z;
else if(z==ans) pans++;
}
}
printf("%lld %lld
",pans,ans);
return 0;
}
思路
建反边,先在反边跑一边bfs找出可以走的边,然后在正着bfs即可。记得以前发过一篇比较详细的博客
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=300000;
int S,T,n,m,head1[N],head2[N],js1,js2;
int vis[5][N];
struct node {
int v,nxt;
}e1[N],e2[N];
void add1(int u,int v) {
e1[++js1].v=v;e1[js1].nxt=head1[u];head1[u]=js1;
}
void add2(int u,int v) {
e2[++js2].v=v;e2[js2].nxt=head2[u];head2[u]=js2;
}
queue<int>q;
int dis[N];
void bfs1() {
vis[1][T]=1;
while(!q.empty()) q.pop();
q.push(T);
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head2[u];i;i=e2[i].nxt) {
int v=e2[i].v;
if(!vis[1][v]) q.push(v),vis[1][v]=1;
}
}
}
void bfs2() {
dis[S]=0;
while(!q.empty()) q.pop();
if(vis[2][S]==0)
q.push(S);
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head1[u];i;i=e1[i].nxt) {
int v=e1[i].v;
if(vis[2][v]==1) continue;
if(dis[v]==-1) dis[v]=dis[u]+1,q.push(v);
if(v==T) return;
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i) {
int x,y;
scanf("%d%d",&x,&y);
if(x==y) continue;
add1(x,y);
add2(y,x);
}
scanf("%d%d",&S,&T);
bfs1();
for(int i=1;i<=n;++i) {
for(int j=head1[i];j;j=e1[j].nxt) {
if(vis[1][e1[j].v]==0) {
vis[2][i]=1;
break;
}
}
}
memset(dis,-1,sizeof(dis));
bfs2();
cout<<dis[T];
return 0;
}
思路
这数据给的真的很微妙。数组a这么大,也是很吓人啊。但是仔细一想就可以知道,如果找到的x满足给出a[i]数组的话,那么即使对a[i]数组取模,依然可以满足方程。所以就可以写个读优,然后将a[i]取模即可。然后就是枚举x带入了。如果直接用快速幂计算的话,复杂度达到了(O(nmlog(m)))所以就可以依次将每个x提出来(详见秦九韶算法),就可以O(n)计算了。
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=110,mod=1e9+7;
typedef long long ll;
ll a[N];
int n,m;
ll read() {
ll x=0,f=1; char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) {
x=x*10+c-'0';
x%=mod;
c=getchar();
}
return x*f;
}
ll work(int now,int x) {
ll ans=0;
for(int i=now;i>=1;--i) {
ans=(ans+a[i])*x%mod;
}
return ans+a[0]%mod;
}
int ans[2000000];
int main() {
n=read();m=read();
for(int i=0;i<=n;++i)
a[i]=read();
int js=0;
for(int i=1;i<=m;++i)
if(work(n,i)==0) ans[++js]=i;
printf("%d
",js);
for(int i=1;i<=js;++i) {
printf("%d
",ans[i]);
}
return 0;
}
总结
这年的题目是比较简单的,但是题面里面全是细节,一不注意就100分变0分了。