2016ACM/ICPC亚洲区沈阳站-重现赛
Posted DGUT_FLY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016ACM/ICPC亚洲区沈阳站-重现赛相关的知识,希望对你有一定的参考价值。
求ans(x),ans(1)=a,ans(2)=b,ans(n)=ans(n-2)*2+ans(n-1)+n^4
如果直接就去解。。。很难,毕竟不是那种可以直接化成矩阵的格式,我们也因为这个被卡很长时间
事实上可以把这道式子化成几个基本元素的格式,然后就容易组合了,比如ans(n-2)*2+ans(n-1)+(n-1)^4+4*(n-1)^3+6*(n-1)^2+4*(n-1)^1+1
包含了所有的基本组成形式,化绝对为相对,并且除了一个n-2其他都是n-1
然后就是矩阵乘时间了
#include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <iostream> #include <cstring> using namespace std; typedef long long int ll; const ll mod = 2147493647; //定义矩阵乘法 struct matrix{ ll arr[7][7]; matrix operator*(matrix b){ matrix ans; ll tmp; for(int i=0; i<7; i++) for(int j=0; j<7; j++){ ans.arr[i][j] = 0; for(int k=0; k<7; k++){ tmp = (arr[i][k]*b.arr[k][j])%mod; ans.arr[i][j] = (ans.arr[i][j] + tmp)%mod; } } return ans; } }; //矩阵快速幂 matrix quick_pow(matrix a,ll N){ matrix ans; memset(ans.arr,0,sizeof(ans.arr)); for(int i=0; i<7; i++) ans.arr[i][i] = 1; while(N){ if(N&1) ans = ans*a; a = a*a; N /= 2;; } return ans; } int main(){ matrix a; memset(a.arr,0,sizeof(a.arr)); a.arr[0][1] = 1; a.arr[1][1] = a.arr[1][2] = a.arr[1][6] = 1; a.arr[2][2] = a.arr[2][6] = 1; a.arr[1][0] = 2; a.arr[1][3] = a.arr[1][5] = a.arr[2][3] = a.arr[2][5] = 4; a.arr[1][4] = a.arr[2][4] = 6; a.arr[3][3] = a.arr[4][4] = a.arr[5][5] = a.arr[6][6] = 1; a.arr[3][6] = a.arr[4][6] = a.arr[5][6] = 1; a.arr[3][4] = a.arr[3][5] = 3; a.arr[4][5] = 2; int T; scanf("%d",&T); ll N,aa,bb; while(T--){ scanf("%I64d %I64d %I64d",&N,&aa,&bb); if(N==1) printf("%I64d\\n",aa); else if(N==2) printf("%I64d\\n",bb); else{ matrix ans = quick_pow(a,N-2); ll ANS = 0; ANS = (ANS+ans.arr[1][0]*aa)%mod; ANS = (ANS+ans.arr[1][1]*bb)%mod; ANS = (ANS+ans.arr[1][2]*16)%mod; ANS = (ANS+ans.arr[1][3]*8)%mod; ANS = (ANS+ans.arr[1][4]*4)%mod; ANS = (ANS+ans.arr[1][5]*2)%mod; ANS = (ANS+ans.arr[1][6]*1)%mod; printf("%I64d\\n",ANS); } } return 0; }
注:我不会在电脑写希腊字符,西塔会写成0-
有直径2,高度2的圆柱体杯子,装d高度的水,把杯子倾倒至水正好不倒出,水面碰着杯沿,问此时水面面积
d==0时S=0,样例给出
d>=1时,水面椭圆是宽径为1,长径为2tan0-的椭圆,根据面积公式S=pi*length*width=pi/cos0-出解
0<d<1时,水面被杯底截断,只能用积分做。
把下述直线右边当做水的截面,一个个积分(岂可修!这么简单!为什么比赛时没想到呢!哎,都怪我被体积蒙蔽了)
V=(0<=y<=2)∫S0dy
S0=pi-a+k*1sina
cosa=k/1
k+1=ytan0-
V=(0<=y<=2)∫S0dy
=(0<=y<=2)∫pi-a+cosasinady
=(pi<=y<=a1)1/tan0-*∫(pi-a+cosasina)(-sina)da
a1=arccos(2tan0--1)
手动转换和确定值间太费时了,还是二分把,反正数据量也不是很多,答案是S=s0/sin0-
/*-------------------------------------------- * File Name: HDU 5954 * Author: Danliwoo * Mail: Danliwoo@outlook.com * Created Time: 2016-11-01 22:22:15 --------------------------------------------*/ #include <bits/stdc++.h> using namespace std; double Pi = acos(-1.0); double eps = 1e-100; double tran = 180/Pi; double vd; double V(double a) { return Pi*cos(a) - a*cos(a) + sin(a) - pow(sin(a), 3)/3; } double get(double theta) { double a1 = acos(2*tan(theta)-1); double v = (V(a1) - V(Pi))/tan(theta); return v; } bool eq(double x, double y) { return fabs(x-y) < eps; } double find(double l, double r) { int cnt = 200; if(eq(get(l), vd)) return l; while(cnt--) { double mid = (l+r) / 2; double gd = get(mid); if(eq(gd, vd)) return mid; if(gd > vd) r = mid; else l = mid; } return l; } int main() { int T; scanf("%d", &T); while(T--) { double d; scanf("%lf", &d); vd = d * Pi; if(eq(d, 0)) { printf("0.00000\\n"); continue; } if(d - 1 > 0) { double theta = atan(2.0-d); double ans = Pi/cos(theta); // printf("d>1 theta = %.5f\\n", theta*tran); printf("%.5f\\n", ans); continue; } double theta = find(eps, Pi/4); double a1 = acos(2 * tan(theta) - 1); double S1 = Pi - a1 + cos(a1) * sin(a1); double ans = S1 / sin(theta); printf("%.5f\\n", ans); } return 0; }
n个人给出长度为l,互不相同的猜测序列,每次用色子<只有这6个元素:1,2,3,4,5,6>确定添加到旧序列末尾的元素,谁的序列与序列的后缀相同就赢,问每人的胜利概率
直接就是AC自动机上。。。但是到了这一步就不知道如何转成矩阵来高斯消元
后面才发现应该以主串(色子投出的序列)为分配概率的依据而不是以子串(猜测序列)为分配概率的依据
这样就理解了为什么从根出发,概率是1/实边条数,不是1/实边权值和
非根结点转移到下一层时,概率按照正常的1/6来计算,因为到了这步回溯也算在概率里
非叶子结点(就是某人胜利的点)回溯到根的下一层时,要再乘从根出发到对应点的概率
此外数组的第1维是目的点,第2位才是出发点,因为方程式的意义是从不同点一定概率到达本点的总和
#include <bits/stdc++.h> using namespace std; typedef double db; typedef long long ll; typedef unsigned int uint; typedef unsigned long long ull; const db eps=1e-6; const int N=110; const int M=100010; #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 #define CLR(a, b) memset(a, b, sizeof(a)) struct trie { int sz,val[N],fail[N],tr[N][6]; void init() { sz=0; memset(tr,0,sizeof(tr)); memset(val,0,sizeof(val)); memset(fail,0,sizeof(fail)); } int trie_insert(int *ch) { int w=0; while (*ch!=-1&&tr[w][*ch]) w=tr[w][*ch],ch++; while (*ch!=-1) tr[w][*ch]=++sz,ch++,w=sz; val[w]=1;return w; } void trie_build() { queue<int>Q; for (int i=0;i<6;i++) if (tr[0][i]) Q.push(tr[0][i]); while (!Q.empty()) { int now=Q.front();Q.pop(); if (val[now]) continue ; for (int i=0;i<6;i++) if (tr[now][i]) { fail[tr[now][i]]=tr[fail[now]][i]; Q.push(tr[now][i]); } else tr[now][i]=tr[fail[now]][i]; } } }AC; double p[N][N]; int gauss(int n) { int i,j,k,mxi; db h; for (i=1;i<=n;i++) { mxi=i; for (j=i;j<=n;j++) if (fabs(p[j][i])>fabs(p[mxi][i])) mxi=j; if (fabs(p[mxi][i])<eps) return 0; if (mxi!=i) { for (j=i;j<=n+1;j++) swap(p[i][j],p[mxi][j]); } h=p[i][i]; for (j=i;j<=n+1;j++) p[i][j]/=h; for (j=1;j<=n;j++) if (j!=i) { h=-p[j][i]/p[i][i]; for (k=i;k<=n+1;k++) p[j][k]+=h*p[i][k]; } } return 1; } int s[1000]; int ans[1000]; int main() { int t,n,l; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&l); AC.init(); for(int i=0;i<n;i++) { for(int j=0;j<l;j++) { scanf("%d",s+j);s[j]--; } s[l]=-1; ans[i]=AC.trie_insert(s); } AC.trie_build(); for(int i=0;i<=AC.sz;i++) { s[i]=0; for(int j=0;j<6;j++)if(AC.tr[i][j])s[i]++; } memset(p,0,sizeof(p)); for(int i=1;i<=AC.sz;i++)p[i][i]=1.0; for(int i=0;i<6;i++)if(AC.tr[0][i])p[AC.tr[0][i]][AC.sz+1]+=1.0/s[0];//从根部出发,由于是按主串算的,要均分 for(int i=1;i<=AC.sz;i++) { for(int j=0;j<6;j++)if(AC.tr[i][j])p[AC.tr[i][j]][i]-=1.0/6.0; if(s[i]!=6 && !AC.val[i])//i结点是非叶子且能回溯到边的 { for(int k=0;k<6;k++) { if(AC.tr[0][k])p[AC.tr[0][k]][i]-=1.0*(6-s[i])/6.0/s[0]; } } } gauss(AC.sz); for(int i=0;i<n;i++)printf("%.6f%c",p[ans[i]][AC.sz+1],i==n-1?\'\\n\':\' \'); }getchar();getchar(); return 0; }
一棵树,从1点到其他点的传递方式有2种:通过其他中转站停留P时再出发,连续赶路,其中连续赶路dis的花费是dis^2,问从1点到其他点的最优距离最长的距离
一开始想到直接树DP,但是还要在树上储存数据不好下手
看了下大神博客才知道有斜率优化这东西
dp[i]=min(dp[j]+(dis[i]-dis[j])^2)+P
然后设j比k优,则
dp[j]+(dis[i]-dis[j])^2+P <= dp[k]+(dis[i]-dis[k])^2+P
得到[(dp[j]+dis[j]*dis[j])-(dp[k]+dis[k]*dis[k])] / 2(dis[j]-dis[k]) <=dis[i].
所以我们可以看出以下两点:我们令g[k,j]=(yj-yk)/(xj-xk)
第一:如果上面的不等式成立,那就说j比k优,而且随着i的增大上述不等式一定是成立的,也就是对i以后算DP值时,j都比k优。那么k就是可以淘汰的。
第二:如果 k<j<i 而且 g[k,j]>g[j,i] 那么 j 是可以淘汰的。
假设 g[j,i]<dis[i]就是i比j优,那么j没有存在的价值
相反如果 g[j,i]>dis[i] 那么同样有 g[k,j]>sum[i] 那么 k比 j优 那么 j 是可以淘汰的
然后就单调队列搞斜率dp
至于树上的回溯导致的状态返回问题,把队尾附近的较优方案取出来标注时间戳还有在队位置,放到栈里,遍历子树时从栈放出来入队
//kopyh #include <bits/stdc++.h> #define INF 0x3f3f3f3f #define MOD 1000000007 //#define N 112345 using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N=100005; vector<pii> g[N]; int m; ll dp[N],dist[N]; int tim; int q[N],head,tail; struct tripl { int pos,val,timer; tripl(){} tripl(int pos,int val,int timer){this->pos=pos;this->val=val;this->timer=timer;} }tmp; stack<tripl> st; inline ll dy(int i) { return dp[i]+dist[i]*dist[i]; } inline ll dx(int j,int k) { return 2*(dist[j]-dist[k]); } inline ll f(int j,int k) { return dy(j)-dy(k); } void dfs(int s,int fa) { int curr=tim++; while(head<tail && f(q[head+1],q[head])<=dist[s]*dx(q[head+1],q[head]))head++; dp[s]=dp[q[head]]+(dist[s]-dist[q[head]])*(dist[s]-dist[q[head]])+m; while(head<tail && f(s,q[tail])*dx(q[tail],q[tail-1])<=f(q[tail],q[tail-1])*dx(s,q[tail])) { st.push(tripl(tail,q[tail],curr)); tail--; } q[++tail]=s; int th=head,tt=tail; for(int i=0;i<g[s].size();i++) { int to=g[s][i].first,val=g[s][i].second; if(fa==to)continue; dist[to]=dist[s]+val; head=th,tail=tt; while(!st.empty()) { tmp=st.top(); if(tmp.timer<=curr)break; q[tmp.pos]=tmp.val; st.pop(); } dfs(to,s); } } int main() { int i,j,k,cas,T,t,x,y,z,n; scanf("%d",&T); cas=0; while(T--) { ll maxans=0; scanf("%d%d",&n,&m); for(i=1;i<=n;i++)g[i].clear(); for(i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); g[x].push_back(make_pair(y,z)); g[y].push_back(make_pair(x,z)); } while(!st.empty())st.pop(); head=tim=0,tail=-1; dist[0]=dist[1]=0; dp[1]=-m; q[++tail]=1; int th=head,tt=tail; for(i=0;i<g[1].size();i++) { int to=g[1][i].first,val=g[1][i].second; dist[to]=dist[1]+val; head=th,tail=tt; while(!st.empty()) { tmp=st.top(); if(tmp.timer<=0)break; q[tmp.pos]=tmp.val; st.pop(); } dfs(to,1); } for(i=1;i<=n;i++)maxans=max(maxans,dp[i]); printf("%I64d\\n",maxans); } return 0; }
以上是关于2016ACM/ICPC亚洲区沈阳站-重现赛的主要内容,如果未能解决你的问题,请参考以下文章