zoukankan      html  css  js  c++  java
  • noip模拟赛题解&总结(2015.10.18-2015.10.19)

    Day1:T1

    考场上觉得第二题比较水,于是就果断地跳进第二题的坑了,结果部分分都没来的及拿...

    f[i][l]:用了i-n的数,这段子序列开始位置为l的方案数
    考虑新加的一个数x,设原序列为S,
    那么只会有三种放法:xSx,xxS,Sxx 
    对于限制条件,为了方便先把大于转小于
    考虑三种转移
    对于xSx:
    f[i][l]+=f[i+1][l+1]
    不能有以下限制:
    h[l]<h[r],h[r]<h[l]
    h[x]<=h[l],h[x]<=h[r] (l+1<=x<=r-1)

    对于xxS:
    f[i][l]+=f[i+1][l+2]
    不能有以下限制:
    h[l]<h[l+1] ,h[l+1]<h[l]
    h[x]<=h[l],h[x]<=h[l+1] (l+2<=x<=r)

    对于Sxx:
    f[i][l]+=f[i+1][l]
    不能有以下限制:
    h[r]<h[r-1],h[r-1]<h[r]
    h[x]<=h[r-1],h[x]<=h[r] (l<=x<=r-2)

    对于小于号的条件直接判
    对于小于等于号的条件,开一个二维前缀和数组,
    那么就相当于判矩形内是否有点,有则不成立 


    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int maxn=2010;
    using namespace std;
    int n,m,s[maxn][maxn];long long f[maxn][maxn];bool t[maxn][maxn];char str[5];
    int get(int x1,int y1,int x2,int y2){return s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];}
    
    void prework(){
    	memset(t,0,sizeof(t)),memset(s,0,sizeof(s));
    	for (int i=1,a,b;i<=m;i++){
    		scanf("%d%s%d",&a,str,&b);
    		if (str[0]=='>') swap(a,b),str[0]='<';
    		++s[a][b];
    		if (str[0]=='=') ++s[b][a];
    		else if (!str[1]) t[a][b]=1;
    	}
    	for (int i=1;i<=n*2;i++)
    		for (int j=1;j<=n*2;j++)
    			s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    }
    
    void dp(){
    	memset(f,0,sizeof(f));
    	for (int i=1;i<=(n<<1)-1;i++) f[n][i]=(!t[i][i+1]&&!t[i+1][i]);
    	for (int i=n-1;i;i--)
    		for (int l=1,r=(n-i+1)*2;r<=n*2;l++,r++){
    			if (!t[l][r]&&!t[r][l]&&!get(l+1,l,r-1,l)&&!get(l+1,r,r-1,r))//xSx
    				f[i][l]+=f[i+1][l+1];
    			if (!t[l][l+1]&&!t[l+1][l]&&!get(l+2,l,r,l+1))//xxS
    				f[i][l]+=f[i+1][l+2];
    			if (!t[r-1][r]&&!t[r][r-1]&&!get(l,r-1,r-2,r))//Sxx
    				f[i][l]+=f[i+1][l];
    		}
    	long long ans=f[1][1]%(1ll<<60);
    	if (ans<0) ans+=(1ll<<60);
    	printf("%I64d
    ",ans);
    }
    
    int main(){
    	//freopen("dragon.in","r",stdin); freopen("dragon.out","w",stdout);
    	while (scanf("%d%d",&n,&m)!=EOF) prework(),dp();
    	return 0;
    }


    T2:坑在这题上了,考场上写的是下1/8圆等差数列求和,上1/8圆分成R-R*sqrt(2)/2段暴力求和,最多要算六百万次sqrt,加上边界处理得比较坑,W+T就变成50了。


    正解是考虑上1/8圆,x++时,y有可能-1,也有可能不减。

    怎么避免大数开根呢,画图可知,只要判断(x+1,y-0.5)在圆内还是圆外即可

    判圆内圆外就用x*x+y*y-r*r是小于0还是大于0

    这样就可以通过只算乘法解决了

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int mod=1000000007;
    using namespace std;
    typedef long long ll;
    ll r,r2,lim;int ans;
    
    int main(){
    	//freopen("circle.in","r",stdin);freopen("circle.out","w",stdout);
    	scanf("%I64d",&r),r2=r*r,lim=(int)round(sqrt(1.0*r2/2));
    	ans=((int)round(sqrt(r2-lim*lim)))%mod;
    	if (ans>lim) ans+=lim,ans%=mod;
    	else if (ans<lim) ans=0;
    	ans+=((lim*(lim-1)/2)%mod),ans%=mod;
    	ans+=r,ans%=mod;
    	for (int x=0,y=r;x<lim-1;x++){
    		//if (1ll*x*x+2ll*x+1ll*y*y-y-1ll*r*r+1>=0) y--;
    		if (1.0*x*x+2.0*x+1.0*y*y-1.0*y+5.0/4.0-1.0*r2>0) y--;
    		ans+=y,ans%=mod;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }

    T3:恶心的SPFA题,考场上这题没写部分分,又坑了...

    对于没有怪兽的情况,跑双关键字SPFA即可

    对于怪兽的格子,因为怪兽只有一次伤害,所以对于与怪兽相邻的点,两两连距离为2,伤害为周围其他怪兽的伤害的边即可。

    对于陷阱,就做点权即可。

    细节比较多,还必须写Dijkstra+Heap优化才可以过...

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define fi first
    #define se second
    #define mp(a,b) make_pair(a,b)
    #define PI pair<int,int>
    #define abs(a) (a>=0?a:-(a))
    const int maxn=505,maxq=maxn*maxn*10,maxm=1100000;
    const int dx[]={-maxn,-1,maxn,1};
    using namespace std;
    int n,m,stx,sty,edx,edy,pre[maxm],now[maxn*maxn],son[maxm],tot,st,ed;char map[maxn*maxn],s[maxn][maxn];
    PI dis[maxn*maxn],val[maxm];
    int P(int x,int y){return x*maxn+y;}
    PI D(int x){return mp(x/maxn,x%maxn);}
    void add(int a,int b,int bl,int dis){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=mp(bl,dis);}
    
    bool check(int x){
    	int a=x/maxn,b=x%maxn;
    	if (a>n||a<=0||b>m||b<=0||map[x]=='#') return 0;
    	return 1;
    }
    
    bool near(int a,int b){
    	int x1=D(a).fi,y1=D(a).se,x2=D(b).fi,y2=D(b).se;
    	if (abs(x1-x2)+abs(y1-y2)<=1) return 1;
    	return 0;
    }
    PI operator +(PI a,PI b){return mp(a.fi+b.fi,a.se+b.se);}
    struct node{
    	int x,b,d;
    	node( int x, int b, int d ){this->x=x,this->b=b,this->d=d;}
    	bool operator <(const node & xx)const{
    		if (b!=xx.b) return b>xx.b;
    		return d>xx.d;
    	}
    };
    
    void spfa(){
    	priority_queue<node> q;
    	q.push(node(st,0,0));
    	memset(dis,63,sizeof(dis));
    	dis[st]=mp(0,0);
    	while (!q.empty()){
    		int x=q.top().x;
    		if (x==ed) break;
    		q.pop();
    		for (int y=now[x];y;y=pre[y]){
    			PI tmp=dis[x]+val[y];
    			if (islower(map[son[y]])) tmp.fi+=map[son[y]]-'a'+1;
    			if (dis[son[y]]>tmp){
    				dis[son[y]]=tmp;
    				q.push(node(son[y],tmp.fi,tmp.se));
    			}
    		}
    	}
    	printf("%d %d
    ",dis[ed].fi,dis[ed].se);
    }
    
    void prework(){
    	for (int i=1;i<=n;i++)//not monster
    		for (int j=1;j<=m;j++){
    			int x=P(i,j);
    			if (map[x]=='#'||isupper(map[x])) continue;
    			for (int k1=0;k1<=3;k1++){
    				int x1=x+dx[k1];
    				if (!check(x1)||isupper(map[x1])) continue;
    				int bl=0;
    				for (int k2=0;k2<=3;k2++){
    					int x2=x1+dx[k2];
    					if (!check(x2)) continue;
    					if (isupper(map[x2])) bl+=map[x2]-'A'+1;
    				}
    				add(x,x1,bl,1);
    			}
    		}
    	for (int i=1;i<=n;i++)//monster
    		for (int j=1;j<=m;j++){
    			int x=P(i,j);
    			if (isupper(map[x])){
    				for (int k1=0;k1<=3;k1++){
    					int x1=x+dx[k1];
    					if (!check(x1)) continue;
    					for (int k2=0;k2<=3;k2++) if (k2!=k1){
    						int x2=x+dx[k2];
    						if (!check(x2)) continue;
    						int bl=0;
    						for (int k3=0;k3<=3;k3++){
    							int x3=x2+dx[k3];
    							if (!check(x3)||near(x1,x3)) continue;
    							if (isupper(map[x3])) bl+=map[x3]-'A'+1;
    							
    						}
    						add(x1,x2,bl,2);
    					}
    				}
    			}
    		}
    }
    
    int main(){
    	memset(map,'#',sizeof(map));
    	scanf("%d%d",&n,&m),scanf("%d%d%d%d",&stx,&sty,&edx,&edy),st=P(stx,sty),ed=P(edx,edy);
    	for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
    	for (int i=0;i<=n+1;i++)
    		for (int j=0;j<=m+1;j++)
    			map[P(i,j)]=s[i][j];
    	prework(),spfa();
    	return 0;
    }




    Day2:T1树形DP。

    设f[i][j]表示i号节点子树中距离为j的点的权值和,这可以递推求出。

    然后对于每组询问(p,d),就是子树中距离为d的点和+向上走i步的父亲的子树中距离为d-i的点的和 再减去走了回头路的点即可

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ls (j<<1)
    #define rs ((j<<1)|1)
    const int maxn=280000,maxt=44;
    typedef long long ll;
    using namespace std;
    int n,Q,a[maxn],pow[maxt],dep,dis;ll ans,f[maxn][maxt];
    
    int main(){
    	freopen("dream.in","r",stdin);freopen("dream.out","w",stdout);
    	scanf("%d%d",&n,&Q);
    	pow[0]=1;for (int i=1;i<=maxt-1;i++) pow[i]=pow[i-1]<<1;
    	for (int i=0;i<=maxt-1;i++) if (pow[i]-1==n){dep=i;break;}
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for (int i=pow[dep-1];i<pow[dep];i++) f[i][0]=a[i];
    	for (int i=dep-1;i>=0;i--)
    		for (int j=pow[i-1];j<pow[i];j++){
    			f[j][0]=a[j];
    			for (int k=1;k<=dep;k++)
    				f[j][k]=f[j*2][k-1]+f[j*2+1][k-1];
    		}
    //	for (int i=1;i<=3;i++) printf("%d ",f[3][i]);puts("fuck");
    	for (int i=1,p,d;i<=Q;i++){
    		scanf("%d%d",&p,&d),ans=f[p][d];
    		if (d==0){printf("%d
    ",a[p]);continue;}
    		for (dis=0;pow[dis]<=p;dis++);dis--;
    		if (d>dep+dis-1){puts("0");continue;}
    		if (dis==0){printf("%I64d
    ",ans);continue;}
    		int ed=min(dis,d);
    		for (int j=1;j<ed;j++){
    			ans+=f[p>>j][d-j];
    			//printf("%d ",ans);
    			ans-=f[p>>(j-1)][d-j-1];
    			//printf("%d
    ",ans);
    		}
    		if (d<=dis) ans+=a[p>>d];
    		else{
    			ans+=f[1][d-dis];
    			ans-=f[p>>(dis-1)][d-dis-1];
    		}
    		printf("%I64d
    ",ans);
    	}
    	return 0;
    }

    T2:数论题,大致思路是类比辗转相除法,由f(a,b,l,r)得到f(b,a%b,x,y)就可以loga时间求出,但是推导过程想法很神...只知道推出来是对的,但是还不知道怎么想到这样推...考场上只拿了20分,不应该不拿另外40分部分分。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int inf=2e9+10;
    using namespace std;
    ll a,b,s,l,r;int ans;
    int gcd(int a,int b){return !b?a:gcd(b,a%b);}
    int f(int a,int b,int l,int r){
    	if (!l) return 0;
    	if (r/a>(l-1)/a) return (l-1)/a+1;
    	int t=f(b,a%b,a-r%a,b-r%b);
    	return (l+1ll*b*t-1)/a+1;
    }
    
    int getans(int a,int b,int l,int r){
    	if (!a) return l?inf:0;
    	int t=gcd(a,b);if (r/g==(l-1)/g) return inf;
    	return f(a,b,l,r);
    }
    
    int main(){
    	//freopen("ice.in","r",stdin);freopen("ice.out","w",stdout);
    	scanf("%d",&T);
    	while (T--){
    		scanf("%I64d%I64d%I64d%I64d%I64d",&a,&b,&s,&l,&r);
    		l-=s,r-=s,a%=b;
    		/*if (l>r) r+=b;
    		if (r>=b) r-=b,l-=b;
    		if (r<=b) r+=b,l+=b;*/
    		if(l>r) r+=b; l-=s,r-=s;
    		if(r>=b) r-=b,l-=b; if(r<0) r+=b,l+=b;
    
    		if (l>=0) ans=getans(a,b,l,r);
    		else ans=min(getans(a,b,0,r),getans(a,b,l+b,b-1));
    		if (ans==inf) puts("Poor PPS.");
    		else printf("%d
    ",ans);
    	}
    	return 0;
    }

    T3:考场上看到这题就放弃了,其实就是找黑白连通块数目就可以区分10个人了,剩下两个只要比较黑白区域的比例即可,代码也不是特别长。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mp(a,b) make_pair(a,b)
    #define fi first
    #define se second
    #define PI pair<int,int>
    using namespace std;
    const int maxn=905,maxq=maxn*maxn*5;
    const int dx[]={1,1, 1,0, 0,-1,-1,-1};
    const int dy[]={1,0,-1,1,-1, 1, 0,-1};
    const char name[][15]={"Baekhyun","Chanyeol","Chen","D.O", "Kai","Kris","Lay","Luhan", "Sehun","Suho","Tao","Xiumin"};
    const int bc[12]={9,5,1,1, 2,3,6,5,  5,2,2,5};
    const int wc[12]={2,1,3,2, 13,1,2,8, 2,8,4,2};
    int T,ans[2],cnt[2],cb,n,m,map[maxn][maxn],head,tail;bool bo[maxn][maxn];
    PI q[maxq+10];
    
    void bfs(int stx,int sty,int c){
    	head=0,q[tail=1]=mp(stx,sty),bo[stx][sty]=1;
    	while (head!=tail){
    		if (++head>maxq) head=1;
    		int x=q[head].fi,y=q[head].se;
    		for (int i=0;i<8;i++){
    			int nx=x+dx[i],ny=y+dy[i];
    			if (map[nx][ny]!=c||bo[nx][ny]) continue;
    			if (++tail>maxq) tail=1;
    			q[tail]=mp(nx,ny),bo[nx][ny]=1;
    		}
    	}
    }
    
    int main(){
    	scanf("%d",&T);
    	while (T--){
    		memset(bo,0,sizeof(bo)),memset(map,-1,sizeof(map)),cb=0,ans[0]=ans[1]=0;
    		scanf("%d%d",&n,&m);int col=0,num,x=1,y=0;
    		for (int i=1;i<=m;i++){
    			scanf("%d",&num),col^=1;
    			if (!col) cb+=num;
    			while (num--){
    				++y;if (y>n) y=1,x++;
    				map[x][y]=col;
    			}
    		}
    		for (int i=1;i<=n;i++) for (int j=1;j<=n;j++)
    			if (!bo[i][j]) ans[map[i][j]]++,bfs(i,j,map[i][j]);
    		if (ans[0]==5&&ans[1]==2){
    			if (cb*100<18*n*n) puts("Xiumin");
    			else puts("Sehun");
    			continue;
    		}
    		for (int i=0;i<12;i++)
    			if (ans[0]==bc[i]&&ans[1]==wc[i]){printf("%s
    ",name[i]);break;}
    	}
    	return 0;
    }

  • 相关阅读:
    [再寄小读者之数学篇](2014-05-27 偏导数的计算)
    [再寄小读者之数学篇](2014-05-27 积分估计)
    [再寄小读者之数学篇](2014-05-27 二阶矩阵的不等式)
    [再寄小读者之数学篇](2014-05-27 特征值估计)
    [家里蹲大学数学杂志]第039期高等数学习题集
    [家里蹲大学数学杂志]第038期历史上最伟大的10个方程
    [家里蹲大学数学杂志]第037期泛函分析期末试题
    [再寄小读者之数学篇](2014-05-26 计算行列式)
    [家里蹲大学数学杂志]第036期泛函分析期末试题
    [再寄小读者之数学篇](2014-05-25 矩阵的交换子)
  • 原文地址:https://www.cnblogs.com/thythy/p/5493518.html
Copyright © 2011-2022 走看看