今天真是个奇奇怪怪的考试。。。盲人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
");
else printf("%d
",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
",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
",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。。。
写完题不要划水,再检查一下。。。