2011 Multi-University Training Contest 6 - Host by JLU

打了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\n",ans2[i][1]-ans2[i][0]+eps,ans1[i][1]-ans1[i][0]);
    }
    return 0;
}
View Code

 

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\n",(sa.matrix[0][0]+MOD)%MOD);
    }
    return 0;
}
View Code

 

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\n",ans);
    }
    return 0;
}
View Code

 

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!\n");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!\n");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!\n");continue;}
        if(fabs(A)<eps){printf("NO SOLUTION!\n");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("\n");
            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("\n");
            continue;
        }
        printf("NO SOLUTION!\n");
    }
    return 0;
}
View Code

 

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\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2017-04-05 18:02  free-loop  阅读(185)  评论(0编辑  收藏  举报