打了4hours,做出一道题。。。太菜了。rank:45/107
开场看B,题目看不懂。。。3hours半才发现i<=N-1,不是i<=x-1.然而还是不会。
看到J有人过了,发现是个简单的树形DP,先处理出根节点的贡献,再O(1)向下转移。。于是49min A了J。
然后就没有任何题会了。。。
A?坐标范围这么大?离散化后好像还是无法搞。。。
B?差分?怎么差都差不出。。。
C?不会数论。。。
D?刚开始写了一个自认为正确的DP+矩阵快速幂,然而发现好像无法处理颜色的排列不允许的情况。。。然后推了个公式,发现要用容斥。。弃疗。。。
E?什么玩意。。。
FGHI....没看。。。
A.Apparent Magnitude(离散化+树状数组)
类似于poj数星星的加强版。但是这道题的坐标范围巨大。离散化后还有60000*60000个点。导致考试时没想清楚。。。
先把点的纵坐标和询问的纵坐标离散化。
考虑求矩形(lx,ly,rx,ry)的点的总数和亮度总和,只需要求出矩形(0,ly,lx-1,ry)和(0,ly,rx,ry)的即可。
考虑离线,把询问的lx和rx排序,然后依次求出每个询问的子询问的ans即可。实际上只需要树状数组的更新和求和即可。复杂度O(nlogn).
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-6 # define MOD 112233 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=60005; //Code begin... struct Node{int x, y; double val;}node[N]; struct Q{int x, ly, ry, id, flag;}q[N]; VI v; int tree1[N<<1], ans1[N][2], siz; double tree2[N<<1], ans2[N][2]; bool comp1(Node a, Node b){return a.x<b.x;} bool comp2(Q a, Q b){ if (a.x==b.x) return a.flag<b.flag; return a.x<b.x; } void init(){ mem(tree1,0); v.clear(); FO(i,0,N<<1) tree2[i]=0; } void add1(int x, int val){while (x<=siz) tree1[x]+=val, x+=lowbit(x);} void add2(int x, double val){while (x<=siz) tree2[x]+=val, x+=lowbit(x);} int query1(int x){ int res=0; while (x) res+=tree1[x], x-=lowbit(x); return res; } double query2(int x){ double res=0; while (x) res+=tree2[x], x-=lowbit(x); return res; } int main () { int n, m; while (~scanf("%d%d",&n,&m)) { init(); FOR(i,1,n) scanf("%d%d%lf",&node[i].x,&node[i].y,&node[i].val), v.pb(node[i].y); sort(node+1,node+n+1,comp1); FOR(i,1,m) { scanf("%d%d%d%d",&q[i].x,&q[i].ly,&q[i+m].x,&q[i].ry); v.pb(q[i].ly); v.pb(q[i].ry); q[i].id=q[i+m].id=i; q[i+m].ly=q[i].ly; q[i+m].ry=q[i].ry; q[i].flag=0; q[i+m].flag=1; } sort(v.begin(),v.end()); siz=unique(v.begin(),v.end())-v.begin(); FOR(i,1,n) node[i].y=lower_bound(v.begin(),v.begin()+siz,node[i].y)-v.begin()+1; FOR(i,1,2*m) { q[i].ly=lower_bound(v.begin(),v.begin()+siz,q[i].ly)-v.begin()+1; q[i].ry=lower_bound(v.begin(),v.begin()+siz,q[i].ry)-v.begin()+1; } sort(q+1,q+2*m+1,comp2); int now=1; FOR(i,1,2*m) { if (q[i].flag==0) { while (now<=n&&node[now].x<q[i].x) add1(node[now].y,1), add2(node[now].y,node[now].val), ++now; } else { while (now<=n&&node[now].x<=q[i].x) add1(node[now].y,1), add2(node[now].y,node[now].val), ++now; } ans1[q[i].id][q[i].flag]=query1(q[i].ry)-query1(q[i].ly-1); ans2[q[i].id][q[i].flag]=query2(q[i].ry)-query2(q[i].ly-1); } FOR(i,1,m) printf("%.2lf/%d ",ans2[i][1]-ans2[i][0]+eps,ans1[i][1]-ans1[i][0]); } return 0; }
D.Drawing Pictures(矩阵快速幂)
考试时构造不出矩阵啊,发现自己还是太蠢了。。。
首先可以发现偶数是不可能满足对称且相邻的颜色不同的。所以只可能奇数。
奇数由于要满足对称性,于是考虑折半后就可以消除对称性的要求了,有一点,题目要求不能出现123456这样的颜色序列。由于对称性也不能出现654321的序列。
我们定义10个状态。(0)表示合法序列。 (1)表示以1结尾的合法序列。 (12)表示以2结尾的合法序列。 (123), (1234),(12345),(6),(65),(654),(6543),(65432)同理。
于是有i(0)=(i-1)(0)*5-(i-1)(12345)-(i-1)(65432).
i(1)=(i-1)(0)-(i-1)(1)-(i-1)(65432). 其他可以很轻松的推出来。
然后矩阵快速幂加速这个转移就行了。
还有一个地方需要注意,因为转移矩阵有-1且过程会取模,导致结果可能是负数。需要再+mod%mod.
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-6 # define MOD 112233 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=15; //Code begin... struct Matrix{LL matrix[N][N];}a, sa, unit, kk; Matrix Mul(Matrix a, Matrix b) //矩阵乘法(%MOD) { Matrix c; FOR(i,0,10) FOR(j,0,10) { c.matrix[i][j]=0; FOR(l,0,10) c.matrix[i][j]+=a.matrix[i][l]*b.matrix[l][j]; c.matrix[i][j]%=MOD; } return c; } Matrix Cal(int exp) //矩阵快速幂 { Matrix p=a, q=unit; if (exp==0) return q; while (exp!=1) { if (exp&1) exp--, q=Mul(p,q); else exp>>=1, p=Mul(p,p); } return Mul(p,q); } void init(){ FO(i,0,11) unit.matrix[i][i]=1; kk.matrix[0][0]=6; kk.matrix[0][1]=kk.matrix[0][6]=1; a.matrix[0][0]=5; a.matrix[5][0]=a.matrix[10][0]=-1; a.matrix[0][1]=1; a.matrix[1][1]=a.matrix[10][1]=-1; FOR(i,2,5) a.matrix[i-1][i]=1; a.matrix[0][6]=1; a.matrix[6][6]=a.matrix[5][6]=-1; FOR(i,7,10) a.matrix[i-1][i]=1; } int main () { int n; init(); while (~scanf("%d",&n)) { if (n%2==0) {puts("0"); continue;} n=(n+1)/2; sa=Cal(n-1); sa=Mul(kk,sa); printf("%lld ",(sa.matrix[0][0]+MOD)%MOD); } return 0; }
E.East and West(贪心+树形DP)
如果着眼于一条边每次只能通过一条火车的限制的话,是感觉很繁杂的。
注意到题目说一定存在一条边使得这两个点集分割开。也就是说这条边是必经之路。从这条边出去的火车一定会满足时间不同。那么经过这条边之后是一定不能产生冲突了。
那么我们只需要求出k个火车到这条边的某个点的最小时间就行了。比如 3 3 3 5,由于这条边存在冲突,所以实际上的时间是 3 4 5 6.
然后求出这个点到东部的点的最短距离。贪心一下即可。因为显然通过这条边时间最少的火车去的点距离远。
我们可以通过一次BFS+排序完成。
关键是如何找到这条割边。
可以通过一次树形DP来完成,记size1[x]是x的子树内属于东部的点的个数。size2[x]是x的子树内属于西部的点的个数。如果满足size1[x]=e&&size2[x]=0或者size1[x]=0&&size2[x]=w.就代表这个点一定出现在割边上。
因此总的时间复杂度是O(nlogn).
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-6 # define MOD 112233 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=1000005; //Code begin... struct Edge{int p, next;}edge[N<<1]; int head[N], cnt=1, n, m, e, w; int dis[N], node[N], siz[2][N]; bool vis[N]; queue<int>Q; int belong(int x){ if (x<=e) return 0; if (x>=n-w+1) return 1; return 2; } void add_edge(int u, int v){edge[cnt].p=v; edge[cnt].next=head[u]; head[u]=cnt++;} void dfs(int x, int fa){ for (int i=head[x]; i; i=edge[i].next) { int v=edge[i].p; if (v==fa) continue; dfs(v,x); siz[0][x]+=siz[0][v]; siz[1][x]+=siz[1][v]; } if (belong(x)!=2) siz[belong(x)][x]+=1; } void init(){mem(head,0); cnt=1; mem(vis,0); mem(siz,0);} void BFS(int x){ int u, v; dis[x]=0; Q.push(x); vis[x]=true; while (!Q.empty()) { u=Q.front(); Q.pop(); for (int i=head[u]; i; i=edge[i].next) { v=edge[i].p; if (vis[v]) continue; dis[v]=dis[u]+1; Q.push(v); vis[v]=true; } } } int main () { int u, v, p, K; while (~scanf("%d%d%d%d",&n,&m,&e,&w)) { init(); FOR(i,1,m) scanf("%d%d",&u,&v), add_edge(u,v), add_edge(v,u); dfs(1,0); FOR(i,1,n) if ((siz[0][i]==e&&siz[1][i]==0)||(siz[0][i]==0&&siz[1][i]==w)) {p=i; break;} BFS(p); scanf("%d",&K); FOR(i,1,K) scanf("%d",&u), node[i]=dis[u]; sort(node+1,node+K+1); sort(dis+n-w+1,dis+n+1); int now=node[1]; FOR(i,1,K) { if (node[i]<now) node[i]=now; else now=node[i]; ++now; } int ans=0; FOR(i,1,K) ans=max(node[i]+dis[n-w+K-i+1], ans); printf("%d ",ans); } return 0; }
F.Find the Circle(解方程)
这题很强。。。可以推出一个三元二次方程组,然后随便乱减再代一下,得到一个异常麻烦的公式。。。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-4 # define MOD 112233 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; inline int Scan() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=1000005; //Code begin... inline double sqr(double a) { return a*a; } double ax, x2, x3, ay, y2, y3, r1, r2, r3; int m1, m2, m3; bool check(double x, double y, double r) { if(fabs(sqr(x-ax)+sqr(y-ay)-sqr(r+m1*r1))>eps)return false; if(fabs(sqr(x-x2)+sqr(y-y2)-sqr(r+m2*r2))>eps)return false; if(fabs(sqr(x-x3)+sqr(y-y3)-sqr(r+m3*r3))>eps)return false; return true; } int main() { int t; scanf("%d", &t); while(t-->0) { scanf("%lf%lf%lf%d%lf%lf%lf%d%lf%lf%lf%d", &ax, &ay, &r1, &m1, &x2, &y2, &r2, &m2, &x3, &y3, &r3, &m3); if(m1==0)m1=-1; if(m2==0)m2=-1; if(m3==0)m3=-1; double a, b, c, d, aa, bb, cc, dd; //ax+by=cr+d a=2*(ax-x2); b=2*(ay-y2); c=2*(r2*m2-r1*m1); d=m2*m2*r2*r2-m1*m1*r1*r1-x2*x2+ax*ax-y2*y2+ay*ay; aa=2*(ax-x3); bb=2*(ay-y3); cc=2*(r3*m3-r1*m1); dd=m3*m3*r3*r3-m1*m1*r1*r1-x3*x3+ax*ax-y3*y3+ay*ay; double a1, b1, a2, b2; if(fabs(bb*a-aa*b)<eps){printf("NO SOLUTION! ");continue;} a1=(a*cc-c*aa)/(bb*a-aa*b);//y=a1*r+b1; b1=(dd*a-d*aa)/(bb*a-aa*b); if(fabs(b*aa-bb*a)<eps){printf("NO SOLUTION! ");continue;} a2=(cc*b-c*bb)/(b*aa-bb*a);//x=a2*r+b2; b2=(b*dd-bb*d)/(b*aa-bb*a); double A, B, C;//A*r^2+B*r+C=0; A=a2*a2+a1*a1-1; B=2*a2*b2-2*ax*a2+2*a1*b1-2*ay*a1-2*m1*r1; C=b2*b2-2*ax*b2+ax*ax+b1*b1-2*ay*b1+ay*ay-r1*r1; double rr; if(B*B-4*A*C<0){printf("NO SOLUTION! ");continue;} if(fabs(A)<eps){printf("NO SOLUTION! ");continue;} rr=(sqrt(B*B-4*A*C)-B)/2/A; double rx=a2*rr+b2; double ry=a1*rr+b1; if(rr>=-eps&&check(rx, ry, rr)) { printf("%.4lf %.4lf", rx, ry); if(fabs(rr)>=eps)printf(" %.4lf", rr); printf(" "); continue; } rr=(-B-sqrt(B*B-4*A*C))/2/A; rx=a2*rr+b2; ry=a1*rr+b1; if(rr>=-eps&&check(rx, ry, rr)) { printf("%.4lf %.4lf", rx, ry); if(fabs(rr)>=eps)printf(" %.4lf", rr); printf(" "); continue; } printf("NO SOLUTION! "); } return 0; }
J.JLUCPC(树形DP)
可以一次dfs算出根节点的答案,然后再dfs一次向下转移后每个节点的答案。复杂度O(n).
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi 3.1415926535 # define eps 1e-9 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=100005; //Code begin... struct Edge{int p, next, w;}edge[N<<1]; int node[N], head[N], cnt=1, dep[N]; LL val[N], siz[N], sum; void init(){mem(head,0); cnt=1; mem(siz,0); mem(val,0); mem(dep,0);} void add_edge(int u, int v, int w){ edge[cnt].p=v; edge[cnt].next=head[u]; edge[cnt].w=w; head[u]=cnt++; } void dfs(int x, int fa){ siz[x]+=node[x]; for (int i=head[x]; i; i=edge[i].next) { int v=edge[i].p; if (v==fa) continue; dep[v]=dep[x]+edge[i].w; dfs(v,x); siz[x]+=siz[v]; } } void dfs1(int x, int fa){ for (int i=head[x]; i; i=edge[i].next) { int v=edge[i].p; if (v==fa) continue; val[v]=val[x]-siz[v]*edge[i].w+(sum-siz[v])*edge[i].w; dfs1(v,x); } } int main () { int n, u, v, w; while (~scanf("%d",&n)) { sum=0; init(); FOR(i,1,n) scanf("%d",node+i), sum+=node[i]; FO(i,1,n) scanf("%d%d%d",&u,&v,&w), add_edge(u,v,w), add_edge(v,u,w); dfs(1,0); FOR(i,1,n) val[1]+=(LL)node[i]*dep[i]; dfs1(1,0); LL ans=(LL)1<<60; FOR(i,1,n) ans=min(ans,val[i]); printf("%lld ",ans); } return 0; }