zoukankan      html  css  js  c++  java
  • 2019 ICPC Asia Nanjing Regional

    2019 ICPC Asia Nanjing Regional

    A - Hard Problem

    计蒜客 - 42395

    若 n = 10,可以先取:6,7,8,9,10。然后随便从1,2,3,4,5里面选一个都肯定符合题意

    若 n = 9,可以先取:5,6,7,8,9,然后随便从1,2,3,4里面选一个都肯定符合题意。

    所以答案就是后半部分的数量+1

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 100000 + 5;
    
    int T, n;
    
    int main() {
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n);
            if(n & 1)printf("%d
    ",n/2+2);
            else printf("%d
    ", n/2+1);
        }
        return 0;
    }
    

    B.Chessboard

    首先当(n=1)或者(m=1)的时候只有两种方法,从上往下刷或者从下往上刷,特判输出2。

    考虑一般情况。

    怎么样的粉刷策略是非法的?

    当我刷着刷着凸出来一块就是非法的。

    因为我只要凸出来一块,一定能导致某一对涂了色的方块之间的距离大于曼哈顿距离。

    所以合法的情况就是,只能要么一次增加一行,或者增加一列。

    那么从最开始的(1 imes 1)的矩阵变成(n imes m)的矩阵需要(n+m-2)次操作,其中有(n-1)次是行扩张,所以答案为:(C_{n+m-2}^{n-1})

    因为我们每次都是增加一行,或者增加一列,所以终点一定在四个角上,所以最后还要对答案乘以4。

    这里我用(lucas)写的,其实也可以(nlogn)预处理后(O(1))出结果,不过都差不多,不计较。

    #include<bits/stdc++.h>
    using namespace std;
    const int mod = 1e9+7;
    const int maxn = 1e6+10;
    typedef long long ll;
    int n, m;
    ll fact[maxn];
    ll qmi(ll a, ll b, ll p)
    {
    	ll res = 1; res %= p;
    	while(b)
    	{
    		if(b & 1) res = res*a%p;
    		a = a*a%p;
    		b >>= 1;
    	}
    	return res;
    }
    
    ll C(ll n, ll m, ll p)
    {
    	if(m > n) return 0;
    	return ((fact[n]*qmi(fact[m],mod-2,mod))%mod*qmi(fact[n-m],mod-2,mod)%mod);
    }
    
    ll lucas(ll a, ll b, ll p)
    {
    	if(b == 0) return 1;
    	return C(a%p, b%p, p) * lucas(a/p, b/p, p) % p;
    }
    
    void solve()
    {
        scanf("%d%d", &n, &m);
        if(n == 1 || m == 1)
        {
            puts("2");
            return;
        }
    
        ll ans = lucas(n+m-2, n-1, mod);
        ans = (ans*4)%mod;
        printf("%lld
    ", ans);
    }
    
    int main()
    {
        fact[0] = 1;
        for(int i = 1; i <= (int)1e6; i++)
            fact[i] = (fact[i-1]*i)%mod;
        int T; scanf("%d", &T);
        while(T--) solve();
        return 0;
    }
    

    C - Digital Path

    计蒜客 - 42397

    相邻的两个点如果权值相差为1,则从小的向大的连边。然后最终连成的是一个DAG,在DAG上面按照拓扑序DP即可。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 1000000 + 5;
    const int M = 4000005;
    const int mod = 1e9 + 7;
    int head[N], ver[M], nxt[M], edge[M], deg[N], tot;
    int ends[N];
    void add(int x, int y){
        deg[y]++;
        ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
    }
    int n, m, a[1010][1010];
    ll d[N][5];// d[x][4]表示以x为终点,长度大于等于4的路径个数,d[x][3]表示长度等于3的路径个数,其他类似
    inline int id(int x, int y){
        return (x-1)*m+y;
    }
    int main() {
        scanf("%d%d",&n,&m);
        memset(a, 0xcf, sizeof a); //注意这里的初始化
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                int x = id(i, j);
                if(a[i+1][j] == a[i][j] + 1) add(x, id(i+1,j));
                if(a[i][j+1] == a[i][j] + 1) add(x, id(i,j+1));
                if(a[i-1][j] == a[i][j] + 1) add(x, id(i-1,j));
                if(a[i][j-1] == a[i][j] + 1) add(x, id(i,j-1));
            }
        }
        queue<int> q;
        for(int i=1;i<=n*m;i++)if(deg[i] == 0)q.push(i),d[i][1] = 1;
        ll res = 0;
        while(q.size()){
            int x = q.front();q.pop();
            bool flag = true;
            for(int i=head[x];i;i=nxt[i]){
                int y = ver[i];
                d[y][2] = (d[y][2] + d[x][1]) % mod;
                d[y][3] = (d[y][3] + d[x][2]) % mod;
                d[y][4] = (d[y][4] + d[x][3] + d[x][4])%mod;
                if(--deg[y] == 0)q.push(y);
                flag = false;
            }
            if(flag) res = (res + d[x][4])%mod;//如果出度为0则记录到答案中
        }
        printf("%lld
    ",res);
        return 0;
    }
    
    

    F - Paper Grading

    计蒜客 - 42400

    trie树跑dfs建树状数组套权值线段树

    首先将字符串都加到Trie中,然后标记 (f[i]) 为每个字符串的最后一个字符所指定的Trie结点,考虑每次询问,在Trie中向下走 k 次,对于可以找到合法结点的情况,答案就是(f[l..r]) 中,属于该合法结点子树的个数。

    到这里,先不考虑修改操作,可以把Trie按dfs序建立主席树,(dfn[i]) 表示第 i 号结点的dfs序,然后对每个(f[i]), 在第(dfn[f[i]]) 棵权值线段树的 (i) 位置的权值+1,然后每次查询子树区间中有多少个数字在([l,r])的范围内。

    加上修改操作,可以利用树状数组维护权值线段树,交换两个字符串的位置,也就是交换了$ f[i]$,直接在权值线段树中修改即可

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 200000 + 5;
    int n, m, L[30][30], R[30][30], cl, cr;
    int f[N];
    char s[N];
    inline int lowbit(int x){return x & -x;}
    struct SEG{
        struct node{
            int ls,rs;
            int sum;
        }t[N*200];
        int rt[N], tot, maxn, res;
        void update(int &rt, int l, int r, int pos, int val){
            if(!rt) rt = ++tot;
            t[rt].sum += val;
            if(l == r)return;
            int mid = l + r >> 1;
            if(pos <= mid) update(t[rt].ls, l, mid, pos, val);
            else update(t[rt].rs, mid+1, r, pos, val);
        }
        void update(int x, int pos, int v){
            for(;x<=maxn;x+=lowbit(x))update(rt[x],1,n,pos,v);
        }
        void query(int dep, int l, int r, int ql, int qr){
            if(ql > qr) return;
            if(l >= ql && r <= qr){
                for(int i=1;i<=cl;i++) res -= t[L[dep][i]].sum;
                for(int i=1;i<=cr;i++) res += t[R[dep][i]].sum;
                return;
            }
            int mid = l+r>>1;
            if(ql <= mid){
                for(int i=1;i<=cl;i++)L[dep+1][i] = t[L[dep][i]].ls;
                for(int j=1;j<=cr;j++)R[dep+1][j] = t[R[dep][j]].ls;
                query(dep+1,l,mid,ql,qr);
            }
            if(qr > mid){
                for(int i=1;i<=cl;i++)L[dep+1][i] = t[L[dep][i]].rs;
                for(int j=1;j<=cr;j++)R[dep+1][j] = t[R[dep][j]].rs;
                query(dep+1,mid+1,r,ql,qr);
            }
        }
    }seg;
    struct Trie{
        int ch[N][26], tot;
        int dfn[N], cid[N], out[N], cnt;
        int ins(char *s){
            int p = 1, len = strlen(s);
            for(int i=0;i<len;i++){
                int c = s[i] - 'a';
                if(!ch[p][c]) ch[p][c] = ++tot;
                p = ch[p][c];
            }
            return p;
        }
        void dfs(int x){
            dfn[x] = ++cnt, cid[cnt] = x;
            for(int i=0;i<26;i++){
                if(ch[x][i]) dfs(ch[x][i]);
            }
            out[x] = cnt;
        }
        int find(char *s, int k){
            int p = 1;
            for(int i=0;i<k;i++){
                int c = s[i] - 'a';
                if(!ch[p][c]) return -1;
                p = ch[p][c];
            }
            return p;
        }
    }trie;
    
    int main() {
        scanf("%d%d",&n,&m);
        trie.tot = 1;
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            f[i] = trie.ins(s);
        }
        trie.dfs(1);
        seg.maxn = trie.cnt;//maxn为权值线段树的个数
        for(int i=1;i<=n;i++){
            f[i] = trie.dfn[f[i]];//这里将f进一步映射到了Trie结点的dfs序上
            seg.update(f[i], i, 1);
        }
        while(m--){
            int op, l, r, k;
            scanf("%d",&op);
            if(op == 1){
                scanf("%d%d",&l,&r);
                seg.update(f[l], l, -1);
                seg.update(f[r], r, -1);
                seg.update(f[r], l, 1);
                seg.update(f[l], r, 1);
                swap(f[l], f[r]);
            }else {
                scanf("%s%d%d%d",s, &k, &l, &r);
                int pos = trie.find(s, k);
                if(pos == -1){
                    printf("0
    ");continue;
                }
                int x = trie.dfn[pos]-1, y = trie.out[pos];
                cl = cr = 0;
                for(int j=x;j;j-=lowbit(j))L[0][++cl] = seg.rt[j];
                for(int j=y;j;j-=lowbit(j))R[0][++cr] = seg.rt[j];
                seg.res = 0;
                seg.query(0, 1, n, l, r);
                printf("%d
    ",seg.res);
            }
        }
        return 0;
    }
    

    H - Prince and Princess

    计蒜客 - 42402

    a = 1, b = 0, c = 0, 则 res = 0

    a > 1, b = 0, c = 0 则 res = 1

    a > b, b > 0, c = 0, 则最坏情况下需要问 2*b+1个人 “公主在那个房间”,一定会得到一个 b+1 次的答案

    a < b, b > 0, c = 0, 则 每个 b 都可以与a回答一样的答案(因为他们的骗子),所以无法区分,也就无法得到正确结果

    a > b + c, 则类似第三种的处理方式,询问 2*(b+c) +1 次 “公主在那个房间”,出现次数最多的就是答案

    a < b + c, 则无解

    #include <iostream>
    #include <stdio.h>
    #include <algorithm>
    #include <map>
    using namespace std;
    typedef long long ll;
    
    int main() {
    	int a, b, c;
    	scanf("%d%d%d", &a, &b, &c);
    	if(c == 0) {
    		if(b == 0) {
    			puts("YES");
    			if(a == 1) puts("0");
                else puts("1");
    		} else if(a > b) {
    			puts("YES");
    			printf("%d
    ", b + b + 1);
    		} else {
    			puts("NO");
    		}
    	} else {
    		if(a > b + c) {
    			puts("YES");
    			printf("%d
    ", b + c + b + c + 1);
    		} else {
    			puts("NO");
    		}
    	}
    	return 0;
    }
    

    J - Spy

    计蒜客 - 42404

    首先要转换为二分图带权最大匹配问题,然后用KM算法去解决。

    dfs版本的KM,每递归一次只会在相等子图中增加一条边,而每次递归都是极限 (O(M)) 的复杂度,所以当 (M=N^2) 时,复杂度将达到(O(N^4))

    即使加了(slack) 数据记录最小值进行优化,那也无法避免这样的情况。

    BFS版本的KM,每次BFS复杂度是 (O(M)) 的,但每次BFS会扩展(O(N)) 条边,所以总复杂度是(O(N^3))

    参考:https://blog.csdn.net/c20182030/article/details/73330556

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 605;
    
    ll n, a[N],b[N],c[N],p[N];
    ll w[N][N];
    ll lx[N] , ly[N];
    ll match[N];
    ll slack[N];
    bool vy[N];
    ll pre[N];
    void bfs( ll k ){
        ll x , y = 0 , yy = 0 , delta;
        memset( pre , 0 , sizeof(pre) );
        for( ll i = 1 ; i <= n ; i++ ) slack[i] = inf;
        match[y] = k;
        while(1){
            x = match[y]; delta = inf; vy[y] = true;
            for( ll i = 1 ; i <= n ;i++ ){
                if( !vy[i] ){
                    if( slack[i] > lx[x] + ly[i] - w[x][i] ){
                        slack[i] = lx[x] + ly[i] - w[x][i];
                        pre[i] = y;
                    }
                    if( slack[i] < delta ) delta = slack[i] , yy = i ;
                }
            }
            for( ll i = 0 ; i <= n ; i++ ){
                if( vy[i] ) lx[match[i]] -= delta , ly[i] += delta;
                else slack[i] -= delta;
            }
            y = yy ;
            if( match[y] == -1 ) break;
        }
        while( y ) match[y] = match[pre[y]] , y = pre[y];
    }
     
    ll KM(){
        memset( lx , 0 ,sizeof(lx) );
        memset( ly , 0 ,sizeof(ly) );
        memset( match , -1, sizeof(match) );
        for( ll i = 1 ; i <= n ; i++ ){
            memset( vy , false , sizeof(vy) );
            bfs(i);
        }
        ll res = 0 ;
        for( ll i = 1 ; i <= n ; i++ ){
            if( match[i] != -1 ){
                res += w[match[i]][i] ;
            }
        }
        return res;
    }
     
    int main()
    {
        scanf("%lld",&n);
        for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(ll i=1;i<=n;i++) scanf("%lld",&p[i]);
        for(ll i=1;i<=n;i++) scanf("%lld",&b[i]);
        for(ll i=1;i<=n;i++) scanf("%lld",&c[i]);
        for(ll i=1;i<=n;i++){
            for(ll j=1;j<=n;j++){
                ll s=0;
                for(ll k=1;k<=n;k++){
                    if(b[i]+c[j]>a[k]) s+=p[k];
                }
                w[i][j]=s;
            }
        }
        printf("%lld
    ",KM());
        return 0;
    }
    

    K.Triangle:

    这道题看起来比写起来难。

    首先考虑在不在点在不在线上,这属于板子活,原理我也不咋懂,找了个板子抄了一下。

    然后考虑几个特殊情况,(p)点和三角形三个顶点(A,B,C)重合,那么一定是对边的中点。

    想到这里我们也可以发现一个很好的性质。

    假设我的点落在(AB)上,如果点靠(A)更近一点,那么另一个点一定落在(BC)上;如果靠(B)近一点,那么另一个点一定落在(AC)上;如果落在(AB)中点,那么另一个点是三角形的顶点。

    所以把点调整一下,让点落在(AB)靠近(A)的地方,然后二分另一个点距离(C)的长度,最后根据长度的比例计算一下点的坐标就可以了。

    因为题目要求(1e-6),所以我设置(eps=1e-8),开(double),不知道有没有别的精度问题。

    需要注意:提前处理出需要用的边的长度,减少库函数的调用。

    #include<bits/stdc++.h>
    using namespace std;
    typedef double ld;
    ld eps = 1e-8;
    
    struct Point{
        ld x, y;
    }a, b, c, p, q;
    ld area;
    ld ab, ac, bc, ap, bp;
    
    bool onSegment(Point Pi, Point Pj, Point Q)
    {
        if((Q.x - Pi.x) * (Pj.y - Pi.y) == (Pj.x - Pi.x) * (Q.y - Pi.y)  //叉乘
           //保证Q点坐标在pi,pj之间
           && min(Pi.x , Pj.x) <= Q.x && Q.x <= max(Pi.x , Pj.x)
           && min(Pi.y , Pj.y) <= Q.y && Q.y <= max(Pi.y , Pj.y))
            return true;
        else
            return false;
    }
    
    ld dist(Point a, Point b){
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    
    bool check(ld mid, ld sinb)
    {
        ld are = sinb*(bp)*(bc-mid)/2;
        if(are*2.0 >= area) return 0;
        return 1;
    }
    
    void solve()
    {
        scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &a.x, &a.y, &b.x, &b.y, &c.x, &c.y, &p.x, &p.y);
        //cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>p.x>>p.y;
        if(!onSegment(a, b, p)&&!onSegment(b, c, p)&&!onSegment(a, c, p))
        {
            puts("-1");
            return;
        }
    
        //先看看会不会在某个点上
        if(p.x == a.x && p.y == a.y)
        {
            //输出bc中点
            q.x = (b.x+c.x)/2.0;
            q.y = (b.y+c.y)/2.0;
            //cout << setprecision(10) << q.x << " " << q.y << endl;
            printf("%.10f %.10f
    ", q.x, q.y);
            return;
        }
        else if(p.x == b.x && p.y == b.y)
        {
            //ac中点
            q.x = (a.x+c.x)/2.0;
            q.y = (a.y+c.y)/2.0;
            //cout << setprecision(10) << q.x << " " << q.y << endl;
            printf("%.10f %.10f
    ", q.x, q.y);
            return;
        }
        else if(p.x == c.x && p.y == c.y)
        {
            //ab中点
            q.x = (a.x+b.x)/2.0;
            q.y = (a.y+b.y)/2.0;
           // cout << setprecision(10) << q.x << " " << q.y << endl;
            printf("%.10f %.10f
    ", q.x, q.y);
            return;
        }
    
    
        //把点弄到ab上
        if(onSegment(a, b, p)) {}
        else if(onSegment(b, c, p)) swap(a, c);
        else if(onSegment(a, c, p)) swap(b, c);
    
        //距离b更近一点
        if(dist(a, p) > dist(b, p)) swap(a, b);
    
        //开始搞
        ab = dist(a, b);
        ac = dist(a, c);
        bc = dist(b, c);
        ap = dist(a, p);
        bp = dist(b, p);
    
        ld p = (ab+bc+ac)/(ld)2.0;
        area = sqrt(p*(p-ab)*(p-bc)*(p-ac));
        ld sina = area*2/ab/bc;
        //area = ab*bc*sinb/2
        //sina = area*2/ab/bc
    
        ld l = 0.0, r = bc;
        while(l+eps < r)
        {
            ld mid = (l+r)/(ld)2.0;
            if(check(mid, sina)) r = mid;
            else l = mid;
        }
    
        ld len = l;
        //输出点
        q.x = (b.x-c.x)*len/bc+c.x;
        q.y = (b.y-c.y)*len/bc+c.y;
        //cout << setprecision(10) << q.x << " " << q.y << endl;
        printf("%.10f %.10f
    ", q.x, q.y);
        return;
    }
    
    int main()
    {
        int T; scanf("%d", &T);
        while(T--) solve();
        return 0;
    }
    
  • 相关阅读:
    初识ES5、ES6
    WEB前端性能优化之三——JavaScript优化
    Web前端浏览器兼容问题
    HTML5新特性
    WEB前端性能优化之二——css优化
    WEB前端性能优化之一——网页级优化
    CSS的一些案例和坑
    bootstrap插件--select2.js--一个基于jQuery的替换框
    boostrap插件---typeahead.js---输入提示下拉菜单
    border-radius:50%,在安卓上存在兼容问题
  • 原文地址:https://www.cnblogs.com/1625--H/p/12468117.html
Copyright © 2011-2022 走看看