[noip][2014]

Day1t1

思路

就是模拟,只是怎样更优雅的模拟而已,不过多点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\n",ansa,ansb);
	return 0;
}

Day1t2

思路

仔细理解题意可以发现,其实题目就是让着对于每个点,将其所能到达的每个点两两相乘,求出所得之和和最大值。如果每个点都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\n",ans,sum);
    return 0;
}

Day1t3

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\n%d\n",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\n%d\n",ans);
	return 0;
}

Day2t1

思路

考虑到数据范围很小,可以枚举每个路口,然后在扫一遍。去看如果将网络发射器安在当前位置会有多少收益,取最大值即可。复杂度\(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\n",pans,ans);
	return 0;
}

Day2t2

思路

建反边,先在反边跑一边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;
}

Day2t3

思路

这数据给的真的很微妙。数组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\n",js);
	for(int i=1;i<=js;++i) {
		printf("%d\n",ans[i]);
	}
	return 0;
}

总结

这年的题目是比较简单的,但是题面里面全是细节,一不注意就100分变0分了。

posted @ 2018-09-29 16:38  wxyww  阅读(123)  评论(0编辑  收藏  举报