2018.7.15模拟赛

今天真是个奇奇怪怪的考试。。。盲人OI+智障OI=HSZOI
T1:(出题人英语水平堪忧)
不会写正解,写的暴力水一下。。。谁曾想有个咸鱼搞错了a,b,x,y的正确关系????80->40???
暴力80解,留个纪念,纪念本咸鱼首次(希望最后一次)写错变量名:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

int exgcd(int a,int b,int &x,int &y) {
	if(!b) {x=1,y=0;return a;}
	int tp=exgcd(b,a%b,y,x);
	y=y-(a/b)*x;
	return tp;
}
int T,a,b,c;
int main() {
	freopen("fuction.in","r",stdin);
	freopen("fuction.out","w",stdout);
	scanf("%d",&T);
	while(T--) {
		int x,y;
		scanf("%d%d%d",&a,&b,&c);
		if(a<b) swap(a,b);
		if(a<0&&b<0&&c<0) a=-a,b=-b,c=-c;
		if(a<0&&b<0&&c>0) {puts("0");continue;}
		if(a>0&&b>0&&c<0) {puts("0");continue;}
		if(a+b>c) {puts("0");continue;}
		if(a+b==c) {
			puts("1");
			continue;
		}
		if(a==1&&b==1) {
				if(c-1>65535)
				printf("ZenMeZheMeDuo\n");
				else printf("%d\n",c-1);
			continue;
		}
		int cnt=0;
		for(int i=1;i*a<c;i++) {
			int tp=c-i*a;
			if(tp%b==0) cnt++;
			if(cnt>65535) break;
		}
		if(cnt>65535) puts("ZenMeZheMeDuo");
		else printf("%d\n",cnt);
	}
}
//3
//-1 -1 -3
//1 1 65536
//1 1 65537

正解:扩欧求方程一组x最小,y最大的解。
代码有注释

#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
void exgcd(int a,int b,int c,int &x,int &y,int &d) {
	if(!b) {
		d=a;
		y=0;
		x=c/a;
		return;
	}
	exgcd(b,a%b,c,y,x,d);
	y-=(a/b)*x;
}
main() {
	int T,a,b,c;
	scanf("%lld",&T);
	while(T--) {
		bool f1=0,f2=0;
		scanf("%lld%lld%lld",&a,&b,&c);
		if(c<0) a=-a,b=-b,c=-c;
		if(a==0&&b==0) {
			if(c==0) puts("ZenMeZheMeDuo");
			else puts("0");
			continue;
		}
		int x,y,gcd;
		if(a<0) a=-a,f1=1;//exgcd不能处理负数
		if(b<0) b=-b,f2=1;
		exgcd(a,b,c,x,y,gcd);
		if(a*x+b*y!=c) {puts("0");continue;}
		if(f1) x=-x,a=-a;
		if(f2) y=-y,b=-b;
		if(a==0) {
			if(y<=0) puts("0");
			else puts("ZenMeZheMeDuo");
			continue;
		}
		if(b==0) {if(x<=0)puts("0");else puts("ZenMeZheMeDuo");continue;}
		if((a>0&&b<0)||(a<0&&b>0)) {puts("ZenMeZheMeDuo");continue;}
		if(a<0) a=-a,b=-b,c=-c;
		a/=gcd,b/=gcd,c/=gcd;
		x%=b;//ax+by=c的一组解,将x缩到最小正数后y的解
		int ans=0;
		if(x<=0) x+=b;
		y=(c-a*x)/b;
		int miny=y%a;//求miny->y之间有多少种合法解。
		if(miny<=0) miny+=a;
		if(miny>y)ans=0;
		else ans=(y-miny)/a+1;
		if(ans>65535) puts("ZenMeZheMeDuo");
		else printf("%lld\n",ans);
	}
}
//3
//-1 -1 -3
//1 1 65536
//1 1 65537

T2
我真的是。。。
没错今天是个奇奇怪怪的考试,又一次变量名搞错。。。难道是昨天被篮球砸了一下脑子不正常了???
正解:树形dp,每个子树上的点必定和别的点有一条边经过该子树与其父节点的边。据此列出状态转移方程即可。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define int long long
using namespace std;
const int N=2005;
int n,m,f[N][N],head[N],ecnt,siz[N],tp[N];
struct Edge {
    int to,nxt,val;
} e[N<<1];
void add(int bg,int ed,int val) {
    e[++ecnt].nxt=head[bg];
    e[ecnt].to=ed;
    e[ecnt].val=val;
    head[bg]=ecnt;
}
void dfs(int x,int fa) {
    siz[x]=1;
    for(int i=head[x]; i; i=e[i].nxt) {
        int v=e[i].to;
        if(v==fa) continue;
        dfs(v,x);
        memset(tp,0,sizeof tp);
        for(int k=0;k<=min(siz[x],m);k++) {
            for(int j=0;j<=min(siz[v],m-k)&&k+j<=m;j++) {
                tp[k+j]=max(tp[k+j],f[x][k]+f[v][j]+e[i].val*((m-j)*j+(n-m-siz[v]+j)*(siz[v]-j)));
            }
        }
        for(int j=0;j<=m;j++) f[x][j]=max(f[x][j],tp[j]);
        siz[x]+=siz[v];
    }
}
signed  main() {
    scanf("%lld%lld",&n,&m);
    for(int i=1,a,b,c; i<n; i++) {
        scanf("%lld%lld%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    dfs(1,0);
    cout<<f[1][m];
}
/*
3 1
1 2 1
1 3 2
*/

T3
并不正确的暴力(教师机10,本机20Orz)
我也懒得调了。。。正解貌似是一个优秀的模拟,什么曼哈顿距离转切比雪夫距离...

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,m,k,g[1005][1005],sx,sy,dx,dy,f[1005][1005][4];
const int mp[3][3]= {1,-1,0,-1,-1,-1,3,-1,2};
int main() {
	freopen("ray.in","r",stdin);
	freopen("ray.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1,x,y; i<=k; i++) {
		scanf("%d%d",&x,&y);
		g[x][y]=1;
	}
	scanf("%d%d",&sx,&sy);
	char dir[5];
	scanf("%s",dir);
	if(dir[0]=='N') {
		dx=-1;
		dir[1]=='E'?dy=1,f[sx][sy][0]=1:dy=-1,f[sx][sy][1]=1;
	} else {
		dx=1;
		dir[1]=='E'?dy=1,f[sx][sy][2]=1:dy=-1,f[sx][sy][3]=1;
	}
	for(int i=0;i<=n+1;i++) g[i][0]=1,g[i][m+1]=1;
	for(int i=0;i<=m+1;i++) g[0][i]=1,g[n+1][i]=1;
	while(1) {
		int nx=dx+sx,ny=dy+sy;
		if(f[nx][ny][mp[dx+1][dy+1]]) break;
		bool flag=0;
		if(g[nx][ny]) {
			flag=1;
			if(mp[dx+1][dy+1]==0) {
				if(g[nx+1][ny]&&!g[nx][ny-1]) dy=-dy,sx--;
				else if((g[nx+1][ny]&&g[nx][ny-1])||(!g[nx+1][ny]&&!g[nx][ny-1])) dx=-dx,dy=-dy,sx+=dx,sy=dy;
				else if(g[nx][ny-1]) dx=-dx,sy++;
			} else if(mp[dx+1][dy+1]==1) {
				if(g[nx+1][ny]&&!g[nx][ny+1]) dy=-dy,sx--;
				else if((g[nx+1][ny]&&g[nx][ny+1])||(!g[nx+1][ny]&&!g[nx][ny+1])) dx=-dx,dy=-dy,sx+=dy,sy+=dy;
				else if(g[nx][ny+1]) dx=-dx,sy--;
			} else if(mp[dx+1][dy+1]==2) {
				if(g[nx-1][ny]&&!g[nx][ny-1]) dy=-dy,sx++;
				else if((g[nx-1][ny]&&g[nx][ny-1])||(!g[nx-1][ny]&&!g[nx][ny-1])) dx=-dx,dy=-dy,sx+=dx,sy+=dy;
				else if(g[nx][ny-1]) dx=-dx,sy++;
			} else if(mp[dx+1][dy+1]==3) {
				if(g[nx+1][ny]&&!g[nx][ny-1]) dy=-dy,sx+=dx;
				else if((g[nx+1][ny]&&g[nx][ny-1])||(!g[nx+1][ny]&&!g[nx][ny-1])) dx=-dx,dy=-dy,sx+=dx,sy+=dy;
				else if(g[nx][ny+-1]) dx=-dx,sy+=dy;
			}
		}
		if(!flag) sx+=dx,sy+=dy;
		if(f[sx][sy][mp[dx+1][dy+1]]) break;
		f[sx][sy][mp[dx+1][dy+1]]=1;
	}
	int cnt=0;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			if(f[i][j][0]||f[i][j][1]||f[i][j][2]||f[i][j][3]) cnt++;
		}
	}
	cout<<cnt;
}
/*
7 5 3
3 3
4 3
5 3
2 1 SE
*/

60pts:(和我的思路有什么区别)

#include <map>
#include <set>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define x first
#define y second
#define pii pair<int,int>
using namespace std;
typedef long long ll;
const int N=100005;
int n,m,k,ans;
char ss[5];
set<pii>bl,em;
map<pii,set<pii> >vis;
pii s,t,dir;
int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<=n+1;i++) bl.insert(pii(i,m+1)),bl.insert(pii(i,0));
	for(int i=0;i<=m+1;i++) bl.insert(pii(n+1,i)),bl.insert(pii(0,i));
	for(int i=1,x,y;i<=k;i++) scanf("%d%d",&x,&y),bl.insert(pii(x,y));
	scanf("%d%d",&s.x,&s.y);
	scanf("%s",ss);
	dir.x=(ss[0]=='N'?-1:1);
	dir.y=(ss[1]=='W'?-1:1);
	while(1) {
		if(em.insert(s).y) ans++;
		t=pii(s.x+dir.x,s.y+dir.y);
		if(!bl.count(t)) s=t;
		else {
			if(!vis[t].insert(dir).y) break;
			int x=bl.count(pii(t.x-dir.x,t.y)),y=bl.count(pii(t.x,t.y-dir.y));
			if(x==y) dir.x=-dir.x,dir.y=-dir.y;
			else if(x) s.x+=dir.x,dir.y=-dir.y;
			else s.y+=dir.y,dir.x=-dir.x;
		}
	}
	cout<<ans;
}

100pts:

//强啊。。。 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <set>
#define int long long
using namespace std;
int n,m,k;
int ans;
bool flag;
const int N=100005;
map<int,int> mp;
map<int,set<int> >A,B;
bool ck(int x,int y) {return mp[x*N+y];}
void work(int dx,int dy,int x,int y) {
	int sx=x,sy=y,nx,ny;
	bool f=1;
	set<int>::iterator it;
	bool bx,by;
	while(1) {
		if(dx+dy) 
			it=A[x-y].lower_bound(x);//哪个对角线上 
		else it=B[x+y].lower_bound(x);
		if(dx==1) nx=*it-1;
		else it--,nx=*it+1;//如果是后退,lower_bound后就找到的不是第一个障碍物,而是下一个。 
		ny=y+dx*dy*(nx-x);//变得横坐标与变得纵坐标差的是个正负。 
		if(!f&&((sx-x)*(sx-nx)<=0&&((x-y==nx-ny&&x-y==sx-sy)||(x+y==nx+ny&&x+y==sx+sy)))){ans+=abs(sx-x);return;}
/*不是第一次运动&&x坐标重复经过过原点(强啊)
(因为可能会因为lower_bound跨越原来的原点)
&&x,y与原点在一条对角线上*/
		ans+=abs(nx-x)+1;//经过的格子数 
		bx=ck(nx+dx,ny),by=ck(nx,ny+dy);//这个点处于3个块夹的或只有一个块 (就是要反弹了)(因为lower_bound找到它的路径上第一个方块) 
		if((bx&&by)||(!bx&&!by)) {flag=1;return;}
		if(bx) dx=-dx,ny+=dy;
		if(by) dy=-dy,nx+=dx;
		x=nx,y=ny;
		f=0;
	}
}
void ins(int  x,int y) {
	mp[x*N+y]=1;
	A[x-y].insert(x);B[x+y].insert(x);
}
int read(){
		//懒得写快读。 
}
main() {
	int sx,sy,dx,dy;
	char opt[5];
	scanf("%lld%lld%lld",&n,&m,&k);
	for(int i=0;i<=n+1;i++) ins(i,0),ins(i,m+1);
	for(int j=0;j<=m+1;j++) ins(0,j),ins(n+1,j);
	for(int i=1,x,y;i<=k;i++) scanf("%lld%lld",&x,&y),ins(x,y);
	scanf("%lld%lld%s",&sx,&sy,opt);
	if(opt[0]=='N') dx=-1;else dx=1;
	if(opt[1]=='W') dy=-1;else dy=1;
	work(dx,dy,sx,sy);
	if(flag) work(-dx,-dy,sx,sy),ans--;
	printf("%lld",ans);
}

总结:
注意保护视力Orz。。。
写完题不要划水,再检查一下。。。

posted @ 2018-07-15 15:09  SWHsz  阅读(110)  评论(0编辑  收藏  举报