这次周赛做了整整5天,最后还是没有AK掉呀,主要是一个博弈,和计算几何的题实在是不理解。今天还是把题总结下吧。
A题:题意一看是最短路,其实比想象的简单。
我一看这题,就写了floyd,虽然复杂,但如果能过的话就没问题。可是竟然被wa了好几次,改来改去,最后还是决定重写。仔细想下,每个点其实就两种情况,从左往右和从右往左,然后取最小值就行。这里标记下,主要是提醒以后题目竟可能想简单点,能简单做就简单做。
代码:
#include<iostream> #include<cstdio> #include<vector> #define maxn 100005 #define ll __int64 using namespace std; vector<int>a[maxn]; ll add[maxn],sub[maxn]; ll val[maxn]; ll max(ll x,ll y) { return x>y?x:y; } void dfs(int u,int fa) { int i,v; for( i=0;i<a[u].size();i++) { v=a[u][i]; if(v==fa) continue; dfs(v,u); add[u]=max(add[u],add[v]); sub[u]=max(sub[u],sub[v]); } ll res=val[u]+add[u]-sub[u]; if(res<0) add[u]-=res; else sub[u]+=res; } int main() { int n,u,v; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); a[u].push_back(v); a[v].push_back(u); } for(int i=1;i<=n;i++) { scanf("%I64d",&val[i]); } dfs(1,-1); printf("%I64d\n",add[1]+sub[1]); return 0; }
B题:题意主要是求一个字典序最小,且未出现过的字符串。
写了个BFS,如果'a'不行,就把与‘a’相连的两个字符入队列。不过由于字符串的字符个数达到3的话,就会超内存,时间也会超。最后的出的答案竟然是只可能有两个字符,这样改了下,就轻松A了,伤不起呀~~~
代码:
#include<iostream> #include<cstdio> #include<string> #include<queue> using namespace std; int n,t; string r; string s[33]; bool check(string tmp) { for(int i=1;i<=n;i++) { if(s[i].find(tmp)!=-1) return 1; } return 0; } void bfs() { queue<string> q; char st[33]; for(int i=0;i<=25;i++) { st[0]=('a'+i); st[1]='\0'; string star1=st; q.push(star1); } for(int i=0;i<=25;i++) { for(int j=0;j<=25;j++) { string star2; st[0]=('a'+i); st[1]=('a'+j); st[2]='\0'; star2=st; q.push(star2); } } while(!q.empty()) { string tm=q.front(); //cout<<tm<<endl; q.pop(); if(check(tm)==0) { r=tm; return ; } } } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { cin>>s[i]; } bfs(); cout<<r<<endl; }
C题:每个人会几种语言,不同人可以通过相同的语言联系起来,求是每个人都能联系起来至少需要学多少种语言。
用并查集A的,不过开始思路不好,写了很长也没A掉,后来换了下简单的思路就A了。就是每次把会相同语言的人连起来,并且,是把后来的那个人当做这个堆的祖先的father。注意是祖先的father。最后找共有几堆就行。
代码:
#include<iostream> #define MAXN 111 int l[MAXN]; int fa[MAXN*MAXN]; using namespace std; int find(int i) { return i==fa[i]?i:find(fa[i]); } int main() { int n,m,mm,flag=0,ans=0,lan; cin>>n>>m; for(int i=1;i<=n;i++) { fa[i]=i; cin>>mm; for(int j=1;j<=mm;j++) { flag=1; cin>>lan; if(l[lan]==0) { l[lan]=i; } else { fa[find(l[lan])]=i; } } } for(int i=1;i<=n;i++) { if(fa[i]==i) ans++; } cout<<ans-flag<<endl; return 0; }
开始题意一直不理解,最后看了别人的博客,才焕然大悟。这题主要是利用数学知识构造这样的点的坐标。首先利用y=x^2的凸性来构造m凸边形。然后把n-m个点再放到不能与前m个点相连能构成更大的凸边形的地方,这里把点都放在特别下面的位置。
这里还得考虑特殊情况,就是m==3,n>4的时候,构成的凸边形至少是4条边的。
代码:
#include<cstdio> #include<cmath> #define pi 3.1415 int x[200],y[200]; int main() { int n,m; scanf("%d%d",&n,&m); if(m==3&&n>4) { printf("-1\n"); return 0; } for(int i=1;i<=m;i++) { printf("%d %d\n",i,1000000+i*i); } for(int i=1;i<=n-m;i++) { printf("%d %d\n",-i,-i*i-1000000); } return 0; }
开始想了下,如果没有初始化一些线的话,可以用对称性原理来做,n==m的话就是一种平衡态。但是如果n!=m的话,又在划分剩余的网格,使得它能构成对称的图形,不过一旦初始化一些边的话,纠结了。本来还想用单位边个数的基偶性,以及边的个数是否为0来判断谁赢谁输,不过都由于很多问题都不能解决而否决了。
博弈的题,用到SG,这个还没入门呀,看代码也不会。纠结~~~
代码暂时没写,waiting。。。
F题:题意是3*3的图中,每个点的数字表示该点和上下左右的四个点改变的次数,然后求最终状态。
比较水的题了,不过由于在最后我想当然的把每个数字之间多加了个空格,一直WA。。。
代码:
#include<cstdio> #include<cstring> #define maxn 10 int a[maxn][maxn]; int map[maxn][maxn]; int n=3; int ni[]= {0,-1,1,0,0};//,-1,1,1,-1}; int nj[]= {0,0,0,-1,1};//,-1,1,-1,1}; bool inmap(int x,int y) { if(1<=x&&x<=3&&1<=y&&y<=3) return 1; else return 0; } void dfs() { for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) { if(a[i][j]==1) { if(map[i][j]==1) map[i][j]=0; else map[i][j]=1; for(int k=1; k<=4; k++) { int nx=i+ni[k],ny=j+nj[k]; if(inmap(nx,ny)) { if(map[nx][ny]==1) map[nx][ny]=0; else map[nx][ny]=1; } } } } } void output() { for(int i=1; i<=n; i++) { for(int j=1; j<n; j++) { printf("%d",map[i][j]); } printf("%d\n",map[i][n]); } } int main() { //while(scanf("%d",&a[1][1])!=EOF) //{ //scanf("%d%d",&a[1][2],&a[1][3]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { map[i][j]=1; } //output(); //while(scanf("%d")) //{ for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) { scanf("%d",&a[i][j]); a[i][j]=a[i][j]%2; } dfs(); output(); //} //} return 0; }
G题:图中任意的黑点到任意其他的黑点的所有路线中只要有一条不用转两次方向则是convex,否则不是。
搜素题,写了个BFS判断是否只有一堆,暴力遍历任意两个点,看这两个点是否在一条直线上,如果是,查这条路上的点是否全黑。否则看构成矩形的四条边,看其中的一组边
是否全黑。
代码:
#include<iostream> #include<cstdio> #define MAXN 55 using namespace std; int a[MAXN][MAXN]; struct p { int x,y; }q[MAXN*MAXN]; bool line(p p1,p p2) { int f,e; if(p1.x==p2.x) { if(p1.y<p2.y) { f=p1.y; e=p2.y; } else { f=p2.y; e=p1.y; } for(int i=f;i<=e;i++) { if(a[p1.x][i]==0) return false; } return true; } else { if(p1.x<p2.x) { f=p1.x; e=p2.x; } else { f=p2.x; e=p1.x; } for(int i=f;i<=e;i++) { if(a[i][p1.y]==0) return false; } return true; } } bool check(int i,int j) { if(q[i].x==q[j].x||q[i].y==q[j].y) { if(line(q[i],q[j])) return true; else return false; } p cur1,cur2; cur1.x=q[i].x,cur1.y=q[j].y; cur2.x=q[j].x,cur2.y=q[i].y; if((line(q[i],cur1)&&line(q[j],cur1))||(line(q[i],cur2)&&line(q[j],cur2))) return true; else return false; } int main() { int n,m; scanf("%d%d",&n,&m); char c[MAXN]; int k=0; for(int i=1; i<=n; i++) { scanf("%s",c); for(int j=0; j<m; j++) { if(c[j]=='W') a[i][j+1]=0; else { a[i][j+1]=1; q[k].x=i,q[k].y=j+1,k++; } } } for(int i=0;i<k;i++) { for(int j=0;j<k;j++) { if(i!=j) { if(check(i,j)==false) { printf("NO\n"); return 0; } } } } printf("YES\n"); return 0; }
H题:题意说从一组数中选最多的数构成一个集合,使得选了i的话,i*k就不能选。
这题可以用并查集做,就是从小到大,依次把具有k倍的关系的数连起来,每次选的时候就选一个,隔一个。这里貌似用了贪心的想法吧,就是选一组有k倍关系的数,从选开头的那个数,然后隔一个选一个是最优的。
这里我没用并查集,其实就把这堆数排了个序,从小到大,看i是否被选,没有的话就选i,把i和i*k都标记了,这样i*k就不能选了。
代码:
#include<iostream> #include<cstdio> #include<map> #include<cstring> #include<algorithm> #define MAXN 100005 #define ll long long using namespace std; map<ll,bool> m; ll a[MAXN]; int main() { int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); if(k==1) { printf("%d\n",n); return 0; } sort(a+1,a+n+1); for(int i=1;i<=n;i++) { if(m[a[i]]==0) { m[a[i]]=1,m[a[i]*k]=1; } } printf("%d\n",m.size()/2); }I题:这题题意开始没看懂,是看代码才懂的。说把一棵树的所有节点的值都修改为0,求操作的次数。修改的方式只能使一棵子树的所有节点加1或者减1。注意这里也可以是子树的一个节点。就是这棵子树的根。
第一次写了下树形dp,树根的状态由子树来决定,感觉和线段树的向上更新,以及树状数组的向上更新有相同的地方。一次就A,比较爽~~~
代码:
#include<iostream> #include<cstdio> #include<vector> #define maxn 100005 #define ll __int64 using namespace std; vector<int>a[maxn]; ll add[maxn],sub[maxn]; ll val[maxn]; ll max(ll x,ll y) { return x>y?x:y; } void dfs(int u,int fa) { int i,v; for( i=0;i<a[u].size();i++) { v=a[u][i]; if(v==fa) continue; dfs(v,u); add[u]=max(add[u],add[v]); sub[u]=max(sub[u],sub[v]); } ll res=val[u]+add[u]-sub[u]; if(res<0) add[u]-=res; else sub[u]+=res; } int main() { int n,u,v; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); a[u].push_back(v); a[v].push_back(u); } for(int i=1;i<=n;i++) { scanf("%I64d",&val[i]); } dfs(1,-1); printf("%I64d\n",add[1]+sub[1]); return 0; }J题:题意是在平面上有n个圆,这n个圆的半径从0开始增大,且与时间成正比,然后就会围成洞,具体看图就明白了,求第一次洞都消失的时间,也就是半径的大小。