山东省第四届ACM省赛
解题报告:http://www.tuicool.com/articles/FnEZJb
A.Rescue The Princess(几何,向量)
B.The number of steps(记忆化搜索,概率dp)
C.Boring Counting(划分树)
D.A-Number and B-Number(数位dp,二分答案)
E.Alice and Bob
F.Mountain Subsequences(dp)
G.Rubik’s cube(哈希+bfs)
H.A^X mod P(转化求幂,打表)
I.Thrall’s Dream(bfs/dfs)
J.Contest Print Server(模拟)
A.Rescue The Princess
d.逆时针给出两个点A、B,求出逆时针构成正三角形的第三个点C。(即A、B、C为逆时针)
s.
法1.
法2.向量旋转公式,证明见:http://www.cnblogs.com/bofengyu/p/5394969.html
c.法1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<math.h> using namespace std; const double PI=4*atan(1); int main(){ int T; double x1,x2,x3; double y1,y2,y3; double theta; double len; scanf("%d",&T); while(T--){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); theta=atan2(y2-y1,x2-x1);//不能用atan(),原因是atan不分辨一、三象限,也不分辨二、四象限 len=sqrt((y2-y1)*(y2-y1)+(x2-x1)*(x2-x1)); x3=x1+len*cos(theta+PI/3); y3=y1+len*sin(theta+PI/3); printf("(%.2f,%.2f)\n",x3,y3); } return 0; }
c2.法2.向量旋转公式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<math.h> using namespace std; const double PI=acos(-1); int main(){ int T; double x1,x2,x3; double y1,y2,y3; double sin60=sin(PI/3); double cos60=cos(PI/3); scanf("%d",&T); while(T--){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); x3=(x2-x1)*cos60-(y2-y1)*sin60+x1; y3=(x2-x1)*sin60+(y2-y1)*cos60+y1; printf("(%.2f,%.2f)\n",x3,y3); } return 0; }
B.The number of steps
C.Boring Counting
s.应该用划分树
c.线段树写的超时了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 线段树 单点更新 */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define L(root) ((root)<<1) #define R(root) (((root)<<1)|1) const int MAXN=50010;// int numbers[MAXN];//初始值 int A,B; struct node{ int left,right;// //int sum; int ma,mi; int mid(){ return left+((right-left)>>1); } }tree[MAXN*4];//4倍空间 void pushUp(int root){ //tree[root].sum=tree[L(root)].sum+tree[R(root)].sum; tree[root].ma=tree[L(root)].ma>tree[R(root)].ma?tree[L(root)].ma:tree[R(root)].ma; tree[root].mi=tree[L(root)].mi<tree[R(root)].mi?tree[L(root)].mi:tree[R(root)].mi; } void build(int root,int left,int right){ tree[root].left=left; tree[root].right=right; if(left==right){ //tree[root].sum=numbers[left]; tree[root].ma=tree[root].mi=numbers[left]; return; } int mid=tree[root].mid(); build(L(root),left,mid); build(R(root),mid+1,right); pushUp(root); } int query(int root,int left,int right){ if(tree[root].left==left&&tree[root].right==right){ //return tree[root].sum; if(A<=tree[root].mi&&tree[root].ma<=B){ return right-left+1; } if(tree[root].left==tree[root].right){ return 0; } int mid=tree[root].mid(); return query(L(root),left,mid)+query(R(root),mid+1,right); } int mid=tree[root].mid(); if(right<=mid){ return query(L(root),left,right); } else if(left>mid){ return query(R(root),left,right); } else{ return query(L(root),left,mid)+query(R(root),mid+1,right); } } /* void update(int root,int pos,int add){ if(tree[root].left==tree[root].right){ tree[root].sum+=add; return; } int mid=tree[root].mid(); if(pos<=mid){ update(L(root),pos,add); } else{ update(R(root),pos,add); } pushUp(root); } */ int main(){ /* memset(numbers,0,sizeof(numbers)); int i; for(i=1;i<MAXN;++i){ numbers[i]=i; } build(1,1,10); cout<<query(1,2,3)<<endl; update(1,2,100); cout<<query(1,2,3)<<endl; */ int T; int N,M; int L,R;//,A,B; int i; int Case=0; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); for(i=1;i<=N;++i){ scanf("%d",&numbers[i]); } build(1,1,N); printf("Case #%d:\n",++Case); for(i=0;i<M;++i){ scanf("%d%d%d%d",&L,&R,&A,&B); printf("%d\n",query(1,L,R)); } } return 0; }
D.A-Number and B-Number
E.Alice and Bob
F.Mountain Subsequences
G.Rubik’s cube
H.A^X mod P
s.在求A^X幂时,直接快速幂求的话,是O(10^6*log(n)*40)=O(10^9) 肯定会超时。
我们将X转化成x=i*k+j。这样先在数组可以表示的范围内找到一个k,然后保存(A^0)~(A^k)的值,然后再将求出(A^k)^i 保存在数组里,
这样每次求A^x,便可以通过这两个数组在O(1)的时间复杂度内求出来,这样时间复杂度就变成了O(10^6*40) = O(4*10^7)了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> using namespace std; #define ll long long #define N 31622 #define M 33333 ll powA[N+10]; ll powB[M+10]; ll n,A,K,a,b,m,P; int cas; void init(){ powA[0]=1; for(int i=1;i<=N;++i){ powA[i]=powA[i-1]*A%P; } ll tmp=powA[N]; powB[0]=1; for (int i=1;i<=M;++i){ powB[i]=powB[i-1]*tmp%P; } } void solve(){ ll fx=K; ll ans=0; for(int i=1;i<=n;++i){ ans=(ans+powB[fx/N]*powA[fx%N]%P)%P; fx=(a*fx+b)%m; } printf("Case #%d: %lld\n",++cas,ans); } int main(){ int T; scanf("%d",&T); cas=0; while (T--){ scanf("%lld%lld%lld%lld%lld%lld%lld",&n,&A,&K,&a,&b,&m,&P); init(); solve(); } return 0; }
I.Thrall’s Dream
d.这个题意我是着实没大看懂啊。。。英语不好。。
N个点,M条有向边,问这个图中任意两点是否可以到达。
s.直接枚举所有点,bfs或者dfs判断是否可达即可。
c.bfs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> #include<queue> using namespace std; #define MAXN 2010 #define MAXM 10010 struct Edge{ int to,next; }edge[MAXM]; int head[MAXN],tot; void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void init(){ tot=0; memset(head,-1,sizeof(head)); } bool d[MAXN][MAXN];//d[i][j]为true表示i可达j bool vis[MAXN]; void bfs(int t){ memset(vis,false,sizeof(vis)); queue<int>myQueue; int u,v; int i; myQueue.push(t); vis[t]=true; while(!myQueue.empty()){ u=myQueue.front(); myQueue.pop(); for(i=head[u];i!=-1;i=edge[i].next){ v=edge[i].to; if(!vis[v]){ d[t][v]=true; vis[v]=true; myQueue.push(v); } } } } int main(){ int T; int N,M; int u,v; int i,j; bool flag;// int Case=0; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); init(); for(i=0;i<M;++i){ scanf("%d%d",&u,&v); addedge(u,v); } memset(d,false,sizeof(d)); for(i=1;i<=N;++i){ bfs(i); } flag=true; for(i=1;i<=N&&flag;++i){ for(j=1;j<=N&&flag;++j){ if(i==j){ continue; } if(d[i][j]||d[j][i]){ continue; } flag=false; break; } } if(flag){ printf("Case %d: Kalimdor is just ahead\n",++Case); } else{ printf("Case %d: The Burning Shadow consume us all\n",++Case); } } return 0; }
c2.dfs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 2010 #define MAXM 10010 struct Edge{ int to,next; }edge[MAXM]; int head[MAXN],tot; void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void init(){ tot=0; memset(head,-1,sizeof(head)); } bool d[MAXN][MAXN];//d[i][j]为true表示i可达j bool vis[MAXN]; void dfs(int t,int t2){ d[t][t2]=true; vis[t2]=true; int v; int i; for(i=head[t2];i!=-1;i=edge[i].next){ v=edge[i].to; if(!vis[v]){ dfs(t,v); } } } int main(){ int T; int N,M; int u,v; int i,j; bool flag;// int Case=0; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); init(); for(i=0;i<M;++i){ scanf("%d%d",&u,&v); addedge(u,v); } memset(d,false,sizeof(d)); for(i=1;i<=N;++i){ memset(vis,false,sizeof(vis)); dfs(i,i); } flag=true; for(i=1;i<=N&&flag;++i){ for(j=1;j<=N&&flag;++j){ if(i==j){ continue; } if(d[i][j]||d[j][i]){ continue; } flag=false; break; } } if(flag){ printf("Case %d: Kalimdor is just ahead\n",++Case); } else{ printf("Case %d: The Burning Shadow consume us all\n",++Case); } } return 0; }
J.Contest Print Server
s.模拟。注意:某个请求没完成的话,要从第一张开始打印(刚开始没细读题,就写成继续打印了)。
ps:when the printed pages counter reached s
这个reached,呵呵,应该是大于吧。
代码写的是当纸张大于等于s时,就更新s并且counter清零的话,wrong了。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<stdio.h> using namespace std; int main(){ int T; int n,s,x,y,mod; int i; char team_name[105][25];//名字 char str[20]; int p[105];//需要纸张数 int counter;//当前已输出记数 scanf("%d",&T); while(T--){ scanf("%d%d%d%d%d",&n,&s,&x,&y,&mod); for(i=0;i<n;++i){ scanf("%s%s%d%s",team_name[i],str,&p[i],str); } counter=0; for(i=0;i<n;++i){ if(counter+p[i]<=s){//可以打印完 printf("%d pages for %s\n",p[i],team_name[i]); counter+=p[i]; } else{//counter+p[i]>s,不能打印完 printf("%d pages for %s\n",s-counter,team_name[i]); s=(s*x+y)%mod; counter=0; while(p[i]>s){//不能打印完 printf("%d pages for %s\n",s,team_name[i]); s=(s*x+y)%mod; counter=0; } printf("%d pages for %s\n",p[i],team_name[i]); counter+=p[i]; } } printf("\n"); } return 0; }