题目链接:The 2019 ACM-ICPC China Shannxi Provincial Programming Contest
A:签到,按花费时间从小到大排个序
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int N=118; 5 int a[N]; 6 int main() 7 { 8 int n,t; 9 while(~scanf("%d%d",&n,&t)) 10 { 11 for(int i=1;i<=n;i++) 12 scanf("%d",&a[i]); 13 sort(a+1,a+1+n); 14 int ans=0; 15 for(int i=1;i<=n;i++) 16 { 17 if(t>=a[i]) 18 { 19 t-=a[i]; 20 ans++; 21 } 22 else 23 break; 24 } 25 printf("%d\n",ans); 26 } 27 return 0; 28 }
L:签到,因为序列里的数都不一样,所以答案只跟n有关,两次同样的操作的话序列就换回来了,所以肯定是两个操作交替进行,然后照着题意打表找规律,注意1和3要特判
1 #include<cstdio> 2 int main() 3 { 4 int n,x; 5 while(~scanf("%d",&n)) 6 { 7 for(int i=0;i<n;i++) 8 scanf("%d",&x); 9 if(n==1) 10 printf("1\n"); 11 else if(n==3) 12 printf("6\n"); 13 else if(n%4==0) 14 printf("4\n"); 15 else if(n%4==1) 16 printf("%d\n",2*n); 17 else if(n%4==2) 18 printf("%d\n",n); 19 else 20 printf("12\n"); 21 } 22 return 0; 23 }
M:二分最短路,首先题意是,给出了一个星系里每个星球的路线图,然后有一艘飞船,它有三个属性,等级和最长的航行距离以及航行次数,初始都是0,然后可以升级,每升一级,花费费用c,最长航行距离增长d,航行次数增加e。如果某条路线的长度大于飞船的最长的航行距离,那么飞船不能走这条路线,并且每走一过一条路就消耗1的航行次数,当航行次数为0,飞船也不能走了。问从1号星球到n号星球的最小费用是多少。
我们就二分升级的次数,那么能走的路线就是长度小于等于次数*d的,在这个限制以每条路线距离为1,求一个1号星球到n号星球的最短距离,判断一下这个距离是不是小于等于次数*e。
1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 typedef long long ll; 5 const int N=100018,inf=1000000007; 6 struct Side{ 7 int v,ne,w; 8 Side(){} 9 Side(int v,int w):v(v),w(w){} 10 bool operator< (const Side &s1)const{ 11 return w>s1.w; 12 } 13 }S[N<<1]; 14 int sn,head[N],dis[N],vis[N]; 15 void init(int n) 16 { 17 sn=0; 18 for(int i=0;i<=n;i++) 19 head[i]=-1; 20 } 21 void add(int u,int v,int w) 22 { 23 S[sn].v=v; 24 S[sn].w=w; 25 S[sn].ne=head[u]; 26 head[u]=sn++; 27 } 28 bool dijk(int n,ll lim,ll num) 29 { 30 priority_queue<Side> q; 31 for(int i=0;i<=n;i++) 32 { 33 vis[i]=0; 34 dis[i]=inf; 35 } 36 dis[1]=0; 37 q.push(Side(1,0)); 38 int u,v; 39 while(!q.empty()) 40 { 41 u=q.top().v; 42 q.pop(); 43 if(u==n) 44 break; 45 if(vis[u]) 46 continue; 47 vis[u]=1; 48 for(int i=head[u];~i;i=S[i].ne) 49 { 50 v=S[i].v; 51 if(S[i].w<=lim&&dis[v]>dis[u]+1) 52 { 53 dis[v]=dis[u]+1; 54 q.push(Side(v,dis[v])); 55 } 56 } 57 } 58 return dis[n]<=num; 59 } 60 int main() 61 { 62 int n,m,c,d,e,u,v,w; 63 while(~scanf("%d%d",&n,&m)) 64 { 65 init(n); 66 scanf("%d%d%d",&c,&d,&e); 67 while(m--) 68 { 69 scanf("%d%d%d",&u,&v,&w); 70 add(u,v,w); 71 add(v,u,w); 72 } 73 int l=0,r=1e5,mid,ans=-1; 74 while(l<=r) 75 { 76 mid=(l+r)>>1; 77 if(dijk(n,1ll*mid*d,1ll*mid*e)) 78 r=mid-1,ans=mid; 79 else 80 l=mid+1; 81 } 82 if(ans==-1) 83 printf("-1\n"); 84 else 85 printf("%lld\n",1ll*ans*c); 86 } 87 return 0; 88 }
C:计算几何。根据题意大概就是下面这个美美的图。
蓝色的线以下除了圆弧外都不能走,所以O2肯定是先走4分之一圆弧,走到l或者r点处,然后如果目标点是A或者C的话,接下来肯定就是l与A的两点之间的距离,或者r与C两点之间的距离,而如果目标点是B点的话,那么就是得先求出两个切点p1,p2了,然后l或r走到p1或p2,最后再是p1或p2走到B的最短距离,求切点就是模板了。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 const double pi=acos(-1.0); 6 struct Point{ 7 double x,y; 8 Point(){} 9 Point(double x,double y):x(x),y(y){} 10 }pr,pa; 11 double r; 12 double dis(Point p1,Point p2){ 13 return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); 14 } 15 void cutp(Point &p1,Point &p2)//求圆外一点和圆的两个切点 16 { 17 Point E,F,G,H; 18 E.x=pa.x-pr.x; 19 E.y=pa.y-pr.y; 20 double temp=r/dis(E,Point(0,0)); 21 F.x=E.x*temp; 22 F.y=E.y*temp; 23 double a=acos(temp),b=-acos(temp); 24 G.x=F.x*cos(a)-F.y*sin(a); 25 G.y=F.x*sin(a)+F.y*cos(a); 26 H.x=F.x*cos(b)-F.y*sin(b); 27 H.y=F.x*sin(b)+F.y*cos(b); 28 p1.x=G.x+pr.x; 29 p1.y=G.y+pr.y; 30 p2.x=H.x+pr.x; 31 p2.y=H.y+pr.y; 32 return ; 33 } 34 double Larc(Point p1,Point p2)//求圆上两点的弧长 35 { 36 double d=dis(p1,p2); 37 double a=acos((r*r+r*r-d*d)*0.5/r/r); 38 return a*r; 39 } 40 double solve() 41 { 42 Point p1,p2; 43 Point rl(pr.x-r,pr.y),rr(pr.x+r,pr.y); 44 if(pa.x<rl.x) 45 return dis(pa,rl); 46 else if(pa.x>rr.x) 47 return dis(pa,rr); 48 else 49 { 50 cutp(p1,p2); 51 double dis1=dis(pa,p1),dis2=dis(pa,p2); 52 return min(min(Larc(rl,p1),Larc(rr,p1))+dis1,min(Larc(rl,p2),Larc(rr,p2))+dis2); 53 } 54 } 55 int main() 56 { 57 int t; 58 scanf("%d",&t); 59 while(t--) 60 { 61 scanf("%lf%lf%lf%lf%lf",&pr.x,&pr.y,&r,&pa.x,&pa.y); 62 printf("%.4f\n",pi*r*0.5+solve()); 63 } 64 return 0; 65 }
D:一个很神奇的背包,坤神教的。首先物品中有对立的存在,所以我们把所有对立的物品分成a类和b类,这样要a[i]的话,b[i]就是另一个人的,对于每一组a,b(假设a>=b),那么两人都有b这部分,就看a-b这部分差距分给谁。那么dp[i],就代表Miku能不能得到分到i的差距,把差距视为体积和价值,要或不要就是背包问题了。最后要差距尽可能的小,那就是从一半的差距开始分,找到第一个可以分到的差距。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 using namespace std; 5 const int N=218; 6 struct Side{ 7 int v,ne; 8 }S[N<<1]; 9 int sn,cnt,head[N],a[N],b[N],c[N],d[N],vis[N]; 10 bool dp[100118]; 11 void init(int n) 12 { 13 sn=cnt=0; 14 for(int i=0;i<=n;i++) 15 { 16 vis[i]=0; 17 head[i]=-1; 18 a[i]=b[i]=0; 19 } 20 } 21 void add(int u,int v) 22 { 23 S[sn].v=v; 24 S[sn].ne=head[u]; 25 head[u]=sn++; 26 } 27 void dfs(int u,int op) 28 { 29 vis[u]=op; 30 for(int i=head[u];~i;i=S[i].ne) 31 if(!vis[S[i].v]) 32 dfs(S[i].v,op*-1); 33 if(op>0) 34 a[cnt]+=c[u]; 35 else 36 b[cnt]+=c[u]; 37 } 38 int main() 39 { 40 int t,n,m,u,v; 41 scanf("%d",&t); 42 while(t--) 43 { 44 scanf("%d%d",&n,&m); 45 init(n); 46 for(int i=1;i<=n;i++) 47 scanf("%d",&c[i]); 48 while(m--) 49 { 50 scanf("%d%d",&u,&v); 51 add(u,v); 52 add(v,u); 53 } 54 for(int i=1;i<=n;i++) 55 if(!vis[i]) 56 { 57 cnt++; 58 dfs(i,1); 59 }//先把物品分类 60 int sumd=0,sumi=0;//sumd保持的是总的差距,差距除100离散化 61 //sumi就是先保持小的总和 62 for(int i=1;i<=cnt;i++) 63 { 64 d[i]=abs(a[i]-b[i])/100; 65 sumd+=d[i]; 66 sumi+=min(a[i],b[i]); 67 } 68 for(int i=0;i<=sumd;i++) 69 dp[i]=false; 70 dp[0]=true; 71 //01背包 72 for(int i=1;i<=cnt;i++) 73 for(int j=sumd;j>=d[i];j--) 74 dp[j]|=dp[j-d[i]]; 75 for(int i=(sumd+1)/2;i<=sumd;i++) 76 if(dp[i]) 77 { 78 printf("%d\n",i*100+sumi); 79 break; 80 } 81 } 82 return 0; 83 }
J:树形dp,题目问的是异或和为0的路径包含在多少条路径之中的和,在下图中如果2到5这条路径的异或和为0,那么它就包含在3*3=9条路径中,也就是size[2]*size[5],size是子树大小。
所以答案的统计可以分成两种,第一种是对于不在一条链上的,贡献的答案就是它们的子树大小的乘积,而第二种就是在同一条链上的就是父代节点上面的部分与子代节点子树部分的乘积。
我们维护一个从根节点到这个节点的异或和,假设为w,开个map,mmp[w]就记录从根节点到这个节点异或和为w的所有子树大小之和。然后,对于一个节点来说,dfs是搜索完子树之和再操作它,所以这个mmp[w]可能就包含了它子树的贡献值,这部分对第一种情况没有贡献,所以我们记录下dfs到它时的mmp,这就是不包含它子树的,然后对于第二种情况,在进入它子树的每一条链前我们就记录下mmp[w],在搜索完这条链后,新的mmp[w]与旧的差值就是它子代节点的贡献值。
1 #include<cstdio> 2 #include<map> 3 using namespace std; 4 typedef long long ll; 5 const int N=100018,mod=1000000007; 6 struct Side{ 7 ll w; 8 int v,ne; 9 }S[N]; 10 int n,sn,head[N],size[N]; 11 ll ans; 12 map<ll,ll> mmp; 13 void init() 14 { 15 sn=ans=0; 16 mmp.clear(); 17 for(int i=0;i<=n;i++) 18 { 19 size[i]=1; 20 head[i]=-1; 21 } 22 } 23 void add(int u,int v,ll w) 24 { 25 S[sn].w=w; 26 S[sn].v=v; 27 S[sn].ne=head[u]; 28 head[u]=sn++; 29 } 30 void dfs(int u,ll w) 31 { 32 ll num1=mmp[w],num2; 33 for(int i=head[u],v;~i;i=S[i].ne) 34 { 35 v=S[i].v; 36 num2=mmp[w]; 37 dfs(v,w^S[i].w); 38 size[u]+=size[v]; 39 ans+=1ll*(n-size[v])*(mmp[w]-num2);//第二种情况与它子树的链的 40 if(ans>=mod) 41 ans%=mod; 42 } 43 ans+=1ll*num1*size[u];//第一种情况,与其他链的 44 if(ans>=mod) 45 ans%=mod; 46 mmp[w]+=size[u]; 47 if(mmp[w]>=mod) 48 mmp[w]%=mod; 49 } 50 int main() 51 { 52 int f; 53 ll w; 54 while(~scanf("%d",&n)) 55 { 56 init(); 57 for(int i=2;i<=n;i++) 58 { 59 scanf("%d%lld",&f,&w); 60 add(f,i,w); 61 } 62 dfs(1,0ll); 63 printf("%lld\n",ans); 64 } 65 return 0; 66 }
最后E,坤神已经补出来了,等我补了再更。