Static Oneplus 不可控制论

2011/05/20 - by Oneplus • ACM GroupHCPC 2011 Spring

HCPC 2011 Spring On-Site Contest 解题报告


在解题报告的最前面,要对上一篇的中一句很不负责任的评论表达十分的歉意。出现这样的失误,完全是我没有认真组织题目校验,对于命题人投的题没有合理组织照成版本混乱,传达要求不清晰明确造成的。以后不管是我还是范利鑫,都要注意避免这样的问题发生。

_____________________________解题报告分割线_____________________________

网络赛C题的解题报告已经更新,想看的同学请移步这里

_____________________________解题报告分割线_____________________________

赛前草草的测试赛中,70学长给出的各题目难度分布如下(感谢70):

PidTitleAuthorCategoryExpected Difficulty(5)
A Best Fit Ringjerrybond线段树DP3
B Seven RoadDarkgt随机化几何3.5
C Shortest PathDarkgt贪心3
D Power-supply SystemXnozeroBCC缩点、树DP5
E Play Beans BeansLambyy模拟1.5
F Dictionaryyangjing模拟1
G Find the FractionLambyy分数序列、分数逼近5++
H Rectangular GridSRM 1460.5
I Zero PairXnozeroHash、模逆元3

赛前本来计划把Zero Pair放到A。这样,Power-supply System就自然被放到E的位置,Lambyy的模拟题也就不会造成这么多误会。但是在插数据表时被我脑残把I查到网络赛最后一题的位置了。(恩,没错,就是数据库pid一项出现重复,而且pid居然不是主键却又好像是Status表的外键,额…HOJ的数据库设计)结果就变成了Lambyy的水题都被大家当成难题了。另一件自我批评的是,整个比赛中出现:H数据范围不明,I题input错(后来证实是Zero把新题面给我但我没改),A的Sample错,D的Sample错,C的时限错。╮(╯_╰)╭

_____________________________解题报告分割线_____________________________

Best Fit Ring

[by jerrybond] 计算各点到中心的距离,按距离大小将点排序,合并距离相等的点。问题转化为最大子段和问题。由于本题有动态更新每点的值,普通动态规划解决这个问题的复杂度为O(QC2),对于本题数据范围不适用。需要线段树进行维护。

#include<math.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define Max(x, y) (x)>(y)?(x):(y)

struct Point {
    int x, y, v;
    int dis;
    int id;
}pt[100001];

struct Node {
    int l, r, sum, maxl, maxr, max;
}node[400001], ans;

int id[100001], tot, p[100001];

void save(int s) {
    int l = s<<1, r = (s<<1) + 1;
    node[s].sum = node[l].sum + node[r].sum;
    node[s].maxl = Max(node[l].maxl, node[l].sum + node[r].maxl);
    node[s].maxr = Max(node[r].maxr, node[r].sum + node[l].maxr);
    node[s].max = node[l].maxr + node[r].maxl;
    node[s].max = Max(node[s].max, node[l].max);
    node[s].max = Max(node[s].max, node[r].max);
}

void build(int l, int r, int s) {
    node[s].l = l;
    node[s].r = r;
    if (l == r) {
        node[s].sum = node[s].max = node[s].maxl = node[s].maxr = pt[l].v;
        return;
    } else if (r > l) {
        int mid = (l + r)>>1;
        build(l, mid, s<<1);
        build(mid+1,r, (s<<1)+1);
        save(s);
    }
}

void insert(int l, int r, int s, int v) {
    if (l == node[s].l & node[s].r == r) {
        node[s].sum = node[s].max = node[s].maxl = node[s].maxr = v;
        return;
    } else if (node[s].r > node[s].l) {
        int mid = (node[s].r + node[s].l)>>1;
        if (r <= mid) insert(l,r,s<<1,v);
        else if (l > mid) insert(l,r,(s<<1)+1,v);
        else {
            insert(l, mid, s<<1, v);
            insert(mid+1, r, (s<<1)+1, v);
        }
        save(s);
    }
}

struct Node check(int l, int r, int s) {
    if (l == node[s].l & node[s].r == r)
        return node[s];
    int mid = (node[s].l + node[s].r)>>1;
    if (r <= mid) return check(l, r, s<<1);
    else if (l > mid) return check(l, r, (s<<1)+1);
    else {
        struct Node tmp, lt, rt;
        lt = check(l, mid, s<<1);
        rt = check(mid+1, r, (s<<1)+1);
        tmp.sum = lt.sum + rt.sum;
        tmp.maxl = Max(lt.maxl, lt.sum+rt.maxl);
        tmp.maxr = Max(rt.maxr, rt.sum+lt.maxr);
        tmp.max = lt.maxr + rt.maxl;
        tmp.max = Max(tmp.max, lt.max);
        tmp.max = Max(tmp.max, rt.max);
        return tmp;
    }
}

int cmp(const void*p1, const void*p2) {
    struct Point*a1 = (struct Point*)p1;
    struct Point*a2 = (struct Point*)p2;
    if (a1->dis<a2->dis) return -1;
    else if (a1->dis==a2->dis) return 0;
    else return 1;
}

int main(void) {
    int n, m, i, j, d, x;
    char str[10];

    while (scanf("%d", n)==1) {
        for (i=1;i<=n;i++) {
            scanf("%d %d %d",pt[i].x,&pt[i].y,&pt[i].v);
            pt[i].dis=pt[i].x*pt[i].x + pt[i].y*pt[i].y;
            pt[i].id=i;
            p[i]=pt[i].v;
        }

        qsort(pt+1, n, sizeof(struct Point), cmp);

        j=n;
        tot=1;
        id[pt[1].id]=1;
        for (i=2;i<=j;i++) {
            if (pt[i].dis!=pt[i-1].dis)
                pt[++tot]=pt[i];
            else pt[tot].v+=pt[i].v;
            id[pt[i].id]=tot;
        }

        build(1,tot,1);
        scanf("%d", m);
        for (i=1;i<=m;i++) {
            scanf("%s", str);
            if (str[0]=='C') {
                scanf ("%d %d", d, &x);
                pt[id[d]].v=pt[id[d]].v-p[d]+x;
                p[d]=x;
                insert(id[d],id[d],1, pt[id[d]].v);
            } else {
                ans = check(1,tot,1);
                printf ("%d\n", ans.max);
            }
        }
    }
    return 0;
}

Seven Roads

[by Darkgt]此题是求平面点中共线最多的点数,对于一般的情况需要O(n2logn)的复杂度,枚举一个点需要O(n)复杂度,验证需要O(nlogn)。

此题明确所有的点最多只在7条直线上,因此如果随机选取一个点有大于1/7的概率取到最多点数的直线,如果每组数据只随机取30个点用来验证,在最坏的情况下有(6/7)30≤1%的概率算法错误,由于最多只有30组数据,理论上可以跑对全部数据。当然,适当增加或减小枚举的点数,可以影响程序的正确率和速度。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <ctime>
using namespace std;
struct tt
{
    int x;
    int y;
} a[10001];
bool cmp(const ttb,const tt&c)
{
    if(b.x==c.x) return b.y < c.y;
    return b.x < c.x;
}
double d[10001];
double INF = 1000000000.0;
double eps = 1e-8;
int cal(int n)
{
    int i,j,ans,k,max;
    if(n<3) return n;
    ans = 1;
    int cnt=30;
    srand((unsigned) time(NULL));
    while(cnt--)
    {
        i=rand()%n;
        k = 0;
        for(j = 0; j < n; j++)
        {
            if(i==j) continue;
            if(a[i].x==a[j].x)
                d[k] = INF;
            else
                d[k] = double(a[i].y - a[j].y) / (a[i].x - a[j].x);
            k++;
        }
        sort(d,d+k);
        max = 2;
        for(j = 1; j < k; j++)
        {
            if(fabs(d[j]-d[j-1])<eps )
                max++;
            else
            {
                if(max > ans)
                    ans = max;
                max = 2;
            }
        }
        if(max > ans)
            ans = max;
    }
    if(ans<n/7) while(1);
    return ans;
}
int main()
{
    int n,ca;
    scanf("%d",ca);
    while(ca--)
    {
        scanf("%d",n);
        for(int i = 0; i < n; i++)
            scanf("%d%d",a[i].x,&a[i].y);
        int ans=cal(n);

        printf("%d\n",ans);
    }
    return 0;
}

Shortest Path

[by Darkgt]如果用s[i]表示到达第i个位置所需最少步数,可以发现s[i-1]≤s[i],具有dp的性质,但dp的复杂度无法解决此题。发现s[i]=p的位置是连续的并且由s[i]=p-1的位置转移到,于是每次可以转移s值相等的一个区间,复杂度O(n)。

#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 100005;
int x[M];
int n;
int main()
{
    int ca;
    scanf("%d",ca);
    while(ca--){
        scanf("%d",n);
        for(int i=0;i<n;i++)
            scanf("%d",x[i]);
        int left=0,right=1,nextright,ans;
        for(ans=0;;ans++){
            if(right<=left) {
                ans=-1;
                break;
            }
            if(left<=n-1&n-1<right) break;
            nextright=-1;
            for(int i=left;i<right;i++)
                nextright=max(nextright,i+x[i]+1);
            left=right;
            right=nextright;
        }
        printf("%d\n",ans);
    }
    return 0;
}

Power-supply System

[by sevenzero]这题我和Xnozero合出来防AK的,题目是从CF64的E改的,原题是个n3的树DP,我想了n2的办法于是将原题数据改大了,考虑到刷刷的存在,为了增加难度就加了双连通分量的缩点,好吧,已经基本不可做了。

先缩点,不会的自己去学,值得一提的是双连通分量的缩点分为基于割和基于桥的两种,本来想出基于割的缩点(参考2010杭州regional的G),最后自己都觉得太恶心就换成基于桥的缩点了。

DP部分要用3个数组in、out、inm。

  • in[i][j]为以i为根的子树中有发电站给i供电且与i的距离为j的最小花费,
  • inm[i]=min(in[i][j]) 辅助数组,
  • out[i][j]为以i为根的子树外有发电站给i供电且与i的距离为j或者在子树内有发电站给i供电的最小花费。

对于in[i][j]:

  • j=0时,说明就在i点建站,in[i][j]=sigma(out[son(i).k][j+1])+cost[i];
  • j>0时,说明发电站来自i的某儿子在的子树,对于这个子树来说发电站在这个子树里,其他子树来说在发电站子树外,

故方程有in[i][j]=min(sigma(out[son(i).k][j+1])+in[son(i).k][j-1]-out[son(i).k][j+1]+f[j]),<br />sigma(out[son(i).k][j+1])可以提前算出作为常数使用,故这里也只用一重循环就可解决,保证最后算法是n2的复杂度。 out[i][j]=min(sigma(out[son(i).k][j+1])+f[j],inm[i])。 inm[root]即为最终答案,整个过程可以用记忆化搜索实现。


#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstring>

using namespace std;

#define V 1011
#define E 1000001

int dfn[V], order, anc[V], bridge[V], stk[V], top, cntb, bcc[V], cntbcc;

struct ED
{
    int v;
    ED *next;
} pool[E], *base[V], *topp;

void init()
{
    topp = pool;
    memset(base, 0, sizeof (base));
}

void addedge(int i, int j)
{
    topp->v = j;
    topp->next = base[i];
    base[i] = topp++;
}

bool vis[V], vise[E];

void dfs(int i)
{
    dfn[i] = order++;
    vis[i]=1;
    anc[i] = i;
    stk[top++]=i;
    for (ED *ip = base[i]; ip != NULL; ip = ip->next)
    {
        int j = ip->v;
        if (vis[j])
        {
            if (!vise[ip - pool] & dfn[anc[i]] > dfn[j])
                anc[i] = j;
        }
        else
        {
            vise[ip - pool] = 1;
            vise[(ip - pool)^1] = 1;
            dfs(j);
            if (dfn[anc[i]] > dfn[anc[j]])
                anc[i] = anc[j];
            if (j == anc[j])
            {
                bridge[cntb++] = ip - pool;
                do
                {
                    bcc[stk[--top]]=cntbcc;
                }while(stk[top]!=j);
                cntbcc++;
            }
        }
    }
}
// BCCs are indexed from 0 to cntbcc-1 , bridges are indexed from 0 to cntb-1
void bridge_bcc(int n)
{
    memset(vis, 0, sizeof (vis));
    memset(vise, 0, sizeof (vise));
    cntb = 0;
    cntbcc=0;
    for (int i = 0; i < n; i++)
        if (!vis[i])
        {
            top=0;
            order = 0;
            dfs(i);
            while(top)
                bcc[stk[--top]]=cntbcc;
            cntbcc++;
        }
}

vector<int> ed[V];
int c,d[V],cost[V];
int in[V][V],inm[V],out[V][V];
bool vm[V]={0},vi[V][V]={0},vo[V][V]={0};
int inf(int,int,int);
int inmf(int,int);
int outf(int,int,int);

int inmf(int x,int f)
{
    if(vm[x])
        return inm[x];
    for(int i=0;i<cntbcc;i++)
        if(inm[x]>inf(x,i,f))
        {
            inm[x]=inf(x,i,f);
        }
    vm[x]=1;
    return inm[x];
}

int inf(int i,int j,int f)
{
    if(vi[i][j]) return in[i][j];
    vi[i][j]=1;
    int ret=0;
    for(int k=0;k<ed[i].size();k++)
        if(ed[i][k]!=f)
            ret+=outf(ed[i][k],j+1,i);
    if(!j)
        return in[i][j]=ret+cost[i];
    for(int k=0;k<ed[i].size();k++)
        if(ed[i][k]!=f)
            in[i][j]=min(in[i][j],ret+inf(ed[i][k],j-1,i)-outf(ed[i][k],j+1,i)+d[j]);
    return in[i][j];
}

int outf(int i,int j,int f)
{
    if(j>=cntbcc) return outf(i,cntbcc-1,f);
    if(vo[i][j]) return out[i][j];
    vo[i][j]=1;
    int ret=d[j];
    for(int k=0;k<ed[i].size();k++)
        if(ed[i][k]!=f)
            ret+=outf(ed[i][k],j+1,i);
    out[i][j]=min(inmf(i,f),ret);
    return out[i][j];
}

bool mtx[V][V];
int main()
{
    int n,m;
    while(scanf("%d%d%d",n,&m,&c)!=EOF)
    {
        init();
        int a,b;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",a,&b);
            addedge(a-1,b-1);
            addedge(b-1,a-1);
        }
        for(int i=0;i<n-1;i++)
            scanf("%d",d[i+1]);
        bridge_bcc(n);
        for(int i=0;i<cntbcc;i++) cost[i] = c;
        memset(mtx,0,sizeof(mtx));
        for(int i=0;i<n;i++)
        {
            cost[bcc[i]]++;
            for(ED *ip=base[i];ip;ip=ip->next)
                if(bcc[i]!=bcc[ip->v])
                    mtx[bcc[i]][bcc[ip->v]]=mtx[bcc[ip->v]][bcc[i]]=1;
        }
        for(int i=0;i<n;i++) ed[i].clear();
        for(int i=0;i<cntbcc;i++)
            for(int j=i+1;j<cntbcc;j++)
                if(mtx[i][j])
                {
                    ed[i].push_back(j);
                    ed[j].push_back(i);
                }
        memset(in,0x3f,sizeof(in));
        memset(inm,0x3f,sizeof(inm));
        memset(out,0x3f,sizeof(out));
        memset(vm,0,sizeof(vm));
        memset(vi,0,sizeof(vi));
        memset(vo,0,sizeof(vo));
        printf("%d\n",inmf(0,0));
    }
    return 0;
}

Play Beans Beans

[by Lambyy]水题一枚, 题意可能比较绕, 但自认为题意描述很清楚, 基本按题意模拟下来即可, 第一组Sample 数据即蕴含了本题最大的Trick, 即在每一个Round过程中新产生的空白点, 是不会被进行操作的, 否则Sample就得7了, 相信大部分过了的人都考虑到了这种情况. 定位为简单模拟题, 基本全场题, 没想卡人……

#include <cstdio>
#include <cstring>
const int N = 32;

int flag, w, h;
char map[N][N];
bool f[N][N];

int up (int x, int y, int t) {
    for (int i = x - 1; i >= 0; i--) {
        if (map[i][y] != '.') {
            t = x - i;
            return (map[i][y] - 'A');
        }
    }
    return -1;
}

int down (int x, int y, int t) {
    for (int i = x + 1; i < h; i++) {
        if (map[i][y] != '.') {
            t = i - x;
            return (map[i][y] - 'A');
        }
    }
    return -2;
}

int left (int x, int y, int t) {
    for (int i = y - 1; i >= 0; i--) {
        if (map[x][i] != '.') {
            t = y - i;
            return (map[x][i] - 'A');
        }
    }
    return -3;
}

int right (int x, int y, int t) {
    for (int i = y + 1; i < w; i++) {
        if (map[x][i] != '.') {
            t = i - y;
            return (map[x][i] - 'A');
        }
    }
    return -4;
}

void solve() {
    int i, j, t = 0;
    int t1, t2, t3, t4, x1, x2, x3, x4;
    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            f[i][j] = (map[i][j] == '.');
        }
    }

    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            t1 = t2 = t3 = t4 = -5;
            x1 = x2 = x3 = x4 = 0;
            if (f[i][j]) {
                t1 = up(i, j, x1);
                t2 = down(i, j, x2);
                t3 = left(i, j, x3);
                t4 = right(i, j, x4);
                if (t1 == t2) {
                    t = 1;
                    map[i - x1][j] = '.';
                    map[i + x2][j] = '.';
                }
                if (t1 == t3) {
                    t = 1;
                    map[i - x1][j] = '.';
                    map[i][j - x3] = '.';
                }

                if (t1 == t4) {
                    t = 1;
                    map[i - x1][j] = '.';
                    map[i][j + x4] = '.';
                }

                if (t2 == t3) {
                    t = 1;
                    map[i + x2][j] = '.';
                    map[i][j - x3] = '.';
                }

                if (t2 == t4) {
                    t = 1;
                    map[i + x2][j] = '.';
                    map[i][j + x4] = '.';
                }

                if (t3 == t4) {
                    t = 1;
                    map[i][j - x3] = '.';
                    map[i][j + x4] = '.';
                }
            }
        }
    }
    if (t == 0) {
        flag = 1;
        return;
    }
    solve();
    return;
}

int main() {
    int i, j, cnt;
    while (scanf("%d %d", h, &w) == 2 && w + h) {
        for (i = 0; i < h; i++) scanf("%s", map[i]);

        flag = cnt = 0;
        solve();
        for (i = 0; i < h; i++)
            for (j = 0; j < w; j++)
                cnt += (map[i][j] == '.');
        printf("%d\n", w * h - cnt);
    }
    return 0;
}

Dictionary

处理XML,求到行首距离为Q的标签的属性。可以一边读入一边递归着求。也可以都读入后去掉正文,再递归处理,总之是递归着做,比较简单。校验时觉得属性名有空格太坑又没什么意思,就把有空格的数据去掉。

#include <iostream>
#include <sstream>
#include <cstring>
#include <vector>
using namespace std;

vector<string> S, res;
int tag;
void dfs(int sum, int now) {
    if(now == S.size())
        return;
    string tmp = S[now];
    for( int i = 0; i < tmp.size(); i ++ )
        if( tmp[i] == '<' || tmp[i] == '>' || tmp[i] == ',' ) tmp[i] = ' ';
    stringstream SS(tmp); string name; int len; SS >> name >> len;
    if(sum + len == tag){ res.push_back(name); }
    while(1){
        tmp = S[++ now];
        if(tmp[1] == '/') break;
        else dfs(sum + len, now);
    }
}

int main() {
    int n, m;
    while( scanf( "%d%d", n, &tag ) == 2 ) {
        string buf;
        getchar(); S.clear(); res.clear();
        for( int i = 0; i < n; i ++ ) {
            getline( cin, buf );
            if(buf[0] == '<' & buf[buf.size() - 1] == '>')
                S.push_back(buf);
        }
        int sum = 0, now = 0;
        dfs(sum, now);
        for(int i = 0; i < res.size(); i ++ ) cout << res[i] << endl;
    }
    return 0;
}

Find the Fraction

[by Lambyy]这题基本上算是纯数学题吧, 且在ACM数论中针对分数领域题目出的少之又少, 所以此题算是冷门题.

题目是对n阶Farey Sequence中的第k项, 进行分母不大于m的分数逼近.

所以第一步需要求待逼近分数.

对于求Farey Sequence中项有朴素算法, 复杂度是O(n^2)的. 所以对于此题n 10^5的数量级是行不通的.

Farey Sequence性质:

  • 性质1  如果a/b,x/y是Fn中的相邻两项, 那么ay + 1 = bx.
    证明: 此结论可根据Fn的构造过程得出.
  • 推论1  Fn中任意相邻两项之差大于等于1/n(n-1).
    证明: 假设这两项顺次分别为a/b和p/q, 则 故得结论。

根据推论1,假设f(i)(1≤i≤n2)为Fk中不大于i/n2的项数。则f(i+1)-f(i)≤1。我们只需要在这些值中二分,求出一个满足f(i)=k的i,然后再求出Fn中小于等于i/n2的最大分数。先用O(nloglogn)的时间作计算f(i)的预处理,由于每次计算f(i)的时间复杂度为O(n),二分的次数为O(logn),最后一次求最大分数时,只要枚举所有分母即可,所以本操作的时间复杂度为O(nlogn)。 得出待逼近分数之后(方便下文解释,设为a/b),要进行分数逼近操作。

由FareySequence构造方式可知,第m阶Farey数列包含了分母不大于m的所有分数,所以最终所求即是在第m阶Farey数列中最接近a/b的两个数。Farey数列是有序的,我们知道a/b的准确值是出现在第b阶Farey数列中的,利用Farey数列的生成特点,可以从第b阶Farey数列中的a/b准确值反推出第m阶Farey数列中最逼近a/b的两个数,这里b不小于m。

接下来便是一个递归逼近的过程.

  • 对于a/b, 生成在第b阶Farey数列中它的前一项和后一项, c/d和e/f. 这里根据性质1, 用扩展gcd算法求解.
  • 如果d和f均不大于m,那么c/d和e/f就是答案。由于c/d和e/f是第b阶Farey数列中最逼近a/b的数,也同时存在于第m阶Farey序列中,而第m阶Farey序列不可能有比第b阶Farey序列更好的逼近,这便是递归过程的结束条件。
  • 如果d和f均大于m,那么令a/b等于c/d和e/f分母较小的那个,跳到1,继续递归。
  • 如果d,f中一个大于m,另一个不大于m,这是显然不大于n的是一个答案。不妨设d>m,而f≤m。我们注意到e/f这一个分数在第m阶Farey数列中就已经存在,而在第d级Farey序列中它与c/d相邻,这里m<d<b,由于Farey数列的特点,c/d左边的那一项与e/f共同生成c/d,因此c/d左边的那一项值为(c-e)/(d-f),如果d-f值仍然大于n,考虑到c/d生成之前(c-e)/(d-f)是与e/f相邻的,他左边的一项,(c-2e)/(d-2f),依此类推,我们可以找到k使得(d-kf)<n,这时(c-ke)/(d-k*f) 便是另一个答案。

这样便可以得到满足题意要求的两个分数。

关于本题结论 以及更多关于Farey Sequence 的知识, 请参考论文《一类分数问题的研究》.

#include <cstdio>
#include <cstring>
#include <cmath>
const int N = 100010;
long long n, A[N], m;

long long gcd(long long a, long long b) {
    return b ? gcd(b, a % b) : a;
}

long long Rank (long long a) {
    long long q, t, ans = 0;
    for (q = 1; q <= n; ++q)    A[q] = q * a / n;
    for (q = 1, ans = 0; q <= n; ++q) {
        ans += A[q];
        for (t = q * 2; t <= n; t += q) A[t] -= A[q];
    }
    return ans;
}

long long bsearch(long long k) {
    long long t, lt = 0, rt = n, mid;
    while (lt < rt) {
        mid = (lt + rt) >> 1;
        t = Rank(mid);
        if (t == k) return mid;
        if (t < k)  lt = mid + 1;
        else rt = mid - 1;
    }
    if (Rank(lt) > k & Rank(lt - 1) < k)   return lt - 1;
    else return lt;
}

void search(long long term, long long s, long long p, long long q, long long t1, long long &t2) {
    long long a, b, c, d, e, f, k, t = 1, i;
    t = gcd(s, n), a = s / t, b = n / t;
    t = gcd(p, q), c = p / t, d = q / t;
    if (term == 0) {
        t1 = a, t2 = b;
        return;
    }
    for (i = 1; i < term; i++) {
        k = (long long)((n + b) / (double)d);
        e = k * c - a, f = k * d - b;
        a = c, b = d, c = e, d = f;
    }
    t1 = c, t2 = d;
    return;
}

long long ext_gcd(long long a, long long b, long long x, long long& y) {
    long long t, ret;
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    ret = ext_gcd(b, a % b, x, y);
    t = x, x = y, y = t - a / b * y;
    return ret;
}

void find(long long a,long long b,long long L1,long long &L2,long long &R1,long long &R2) {
    long long x, y;
    ext_gcd(a, b, x, y);
    x = (x + b) % b;
    L2 = x, L1 = (L2 * a - 1) / b;
    R2 = (x * (b - 1)) % b, R1 = (R2 * a + 1) / b;
}

void solve(long long a,long long b,long long L1,long long &L2,long long &R1,long long &R2) {
    if (b <= m) {
        L1 = R1 = a;
        L2 = R2 = b;
        return;
    }
    find (a, b, L1, L2, R1, R2);
    if (R2 <= m & L2 <= m)  return;
    while (R2 > m & L2 > m) {
        if (R2 > L2) {
            a = L1, b = L2;
            find (a, b, L1, L2, R1, R2);
        } else {
            a = R1, b = R2;
            find (a, b, L1, L2, R1, R2);
        }
    }
    long long t1, t2;
    while (L2 > m) {
        a = L1, b = L2;
        find (a, b, L1, L2, t1, t2);
    }
    while (R2 > m) {
        a = R1, b = R2;
        find (a, b, t1, t2, R1, R2);
    }
}

int main() {
//    freopen("F.in", "r", stdin);
//    freopen("F.out", "w", stdout);
    long long i, k, a, p, q, po, t1, t2;
    long long t, Up1, Up2, Down1, Down2;
    double mi, tmp;
    while (scanf ("%lld %lld %lld", n, &k, &m) == 3) {
        a = bsearch(k), mi = 1.00, po = 0;
        for (q = 1; q <= n; q++) {
            p = ((a + 1) * q - 1) / n;
            if (p * n > q * a) {
                tmp = (double) (p) / (double) (q);
                if (tmp < mi)  mi = tmp, po = q;
            }
        }

        if (po != 0) q = po, p = ((a + 1) * q - 1) / n;
        else q = n, p = a + 1;

        search(k - Rank(a), a, p, q, t1, t2);
        t = gcd(t1, t2);
        t1 /= t, t2 /= t;

        solve(t1, t2, Down1, Down2, Up1, Up2);
        printf("%lld/%lld %lld/%lld\n", Down1, Down2, Up1, Up2);
    }
    return 0;
}

Zero Pair

[by Xnozero]a*b=rev(a)*rev(b)转化为a/rev(a)=rev(b)/b,预处理出范围内的 rev(a)的值,将a/rev(a)的分子分母约分后存入MAP,再对b进行类似的处理,从MAP中找到对应的值,统计总数即可。

#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
using namespace std;
map<pair<int,int>,int> hash;

int ext(int a, int b, int x, int &y)
{
    int t1, t2;
    if (!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    else
    {
        t1 = ext(b, a % b, x, y);
        t2 = x;
        x = y;
        y = t2 - (a / b) * y;
        return t1;
    }
}
int gcd(int a, int b)
{
    if (a < b) return gcd(b, a);
    if (b==0) return a;
    return gcd(b, a%b);
}
pair<int,int> work(int a,int b)
{
    return make_pair(a/gcd(a,b),b/gcd(a,b));
}
pair<int,int> temp;
int main()
{
    // freopen("in.txt","r",stdin);
    int a,b,p;
    int x,y;
    int i,j;
    int ans;
    while (scanf("%d %d %d",a,&b,&p)==3)
    {
        ans=0;
        hash.clear();

        for (i=1;i<=a;i++)
        {
            ext(i,p,x,y);
            x=((x%p)+p)%p;
            if(x==0)
                x=p;
            hash[work(i,x)]++;
        }
        for (i=1;i<=b;i++)
        {
            ext(i,p,x,y);
            x=((x%p)+p)%p;
            if(x==0)
                x=p;
            temp=work(x,i);
            if (hash.find(temp)!=hash.end())
            {
                ans+=hash[temp];
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
_____________________________解题报告分割线_____________________________

比赛现场的情况是这样的

比较惊艳的几处:

  • 第二、第四是两位哈三中OI的同学,第二的同学好像是NOI银,全场唯一一个搞定了羊的G,真强!
  • rank 14的同学来自能源学院,寒假时自学过一点C语言,是我程序设计实践的学生,也很强
  • 尤波学长、bin3学长依旧成绩靠前
  • 羊那天说了一句,“后生可畏啊”
  • 刷刷…不说什么了

就这样子吧。

blog comments powered by Disqus