【jsoi】第一季 [略]精简题解

UPD:好像有两道题的代码逃跑了?= =就先不找了,反正都是水题。


 

精简题解系列第四弹。(其实也不是那么精简啊= =)

[JSOI2008]最大数maxnumber

  单点修改,区间最大值查询,裸线段树

 1 /**************************************************************

 2     Problem: 1012

 3     User: wsc500

 4     Language: C++

 5     Result: Accepted

 6     Time:944 ms

 7     Memory:5368 kb

 8 ****************************************************************/

 9  

10 #include <cstdio>

11 #include <cstdlib>

12 #include <iostream>

13 #include <cstring>

14 #include <algorithm>

15  

16 using namespace std;

17 #define MAXN (262144+2)

18 #define Q (262143)

19 #define bigger(a,b) ((a)>(b)?(a):(b))

20  

21 /*Gloable*/

22 int m,d;

23 int T[MAXN*4];

24 /*Function*/

25 void insert(int p,int val){

26     T[p+Q]=val;

27     int i=p+Q;

28     while (i!=0){

29         T[i/2]=bigger(T[i/2*2],T[i/2*2+1]);

30         i/=2;

31     }

32 }

33 int query(int L,int R,int a,int b,int p){

34     //printf("%d %d %d %d %d\n",L,R,a,b,p);

35     if (R>b)  R=b;

36     if (L<a)  L=a;

37     if (L==a&&R==b) return T[p];

38      

39     int mid=(a+b)/2;

40     int ans1,ans2;

41     ans1=ans2=0;

42      

43     if (L<=mid) ans1=query(L,R,a,mid,p*2);

44     if (R>mid) ans2=query(L,R,mid+1,b,p*2+1);

45      

46     return bigger(ans1,ans2);

47 }

48 int main()

49 {

50     memset(T,0,sizeof(T));

51     int x,t=0,n=1;

52     char cmd[10];

53     scanf("%d%d",&m,&d);

54     for (int i=1;i<=m;i++){

55         //printf("hehe\n");

56         scanf("%s%d",cmd,&x);

57         if (cmd[0]=='A'){

58             insert(n,(x+t)%d);

59             n++;

60         }

61         if (cmd[0]=='Q'){

62             //printf("hehe");

63             t=query(n-x,n-1,1,Q+1,1);

64             printf("%d\n",t);

65         }

66     }

67      

68     return 0;

69 }
View Code

[JSOI2008]球形空间产生器sphere

  果然前两道都是水题T_T。题中给出了公式,那么列出n+1个式子果断考虑解方程。首先开方可以忽略,对于平方的问题,相邻两个等式相减,利用平方差可以吧未知数的平方约掉。那么正好n+1个式子得出n和一次方程,高斯消元即可。很少写高斯消元啊,1A真开心= =

 1 /**************************************************************

 2     Problem: 1013

 3     User: wsc500

 4     Language: C++

 5     Result: Accepted

 6     Time:0 ms

 7     Memory:1276 kb

 8 ****************************************************************/

 9  

10 #include <cstdio>

11 #include <cstdlib>

12 #include <iostream>

13 #include <cstring>

14 #include <algorithm>

15 #include <cmath>

16  

17 using namespace std;

18 #define MAXN (10+5)

19 #define bigger(a,b) ((a)>(b)?(a):(b))

20  

21 /*Gloable*/

22 double data[MAXN+1][MAXN];

23 double M[MAXN][MAXN];

24 int n;

25 /*Function*/

26 void readData(){

27     scanf("%d",&n);

28     for (int i=1;i<=n+1;i++){

29         for (int j=1;j<=n;j++){

30             scanf("%lf",&data[i][j]);

31         }

32     }

33 }

34 void setGuass(){

35     for (int i=2;i<=n+1;i++){

36         for (int j=1;j<=n;j++){

37             M[i-1][j]=2.0*(data[i][j]-data[i-1][j]);

38             M[i-1][n+1]+=(data[i][j]+data[i-1][j])*(data[i][j]-data[i-1][j]);

39         }

40     }

41 }

42 void sloveGuass(){

43     for (int i=1;i<=n;i++){

44         int r=i;

45         for (int j=i+1;j<=n;j++){

46             if (fabs(M[j][i])>fabs(M[r][i]))    r=j;

47         }

48         for (int j=1;j<=n+1;j++)  swap(M[i][j],M[r][j]);

49          

50         for (int j=i+1;j<=n;j++){

51             double f=M[j][i]/M[i][i];

52             for (int k=i;k<=n+1;k++){

53                 M[j][k]-=M[i][k]*f;

54             }

55         }

56     }

57      

58     for (int i=n;i>=1;i--){

59         for (int j=i+1;j<=n;j++){

60             M[i][n+1]-=M[i][j]*M[j][n+1];

61         }

62         M[i][n+1]/=M[i][i];

63     }

64 }

65 void printAns(){

66     for (int i=1;i<=n-1;i++){

67         printf("%.3lf ",M[i][n+1]);

68     }

69     printf("%.3lf\n",M[n][n+1]);

70 }

71 int main()

72 {

73     memset(M,0,sizeof(M));

74     readData();

75     setGuass();

76     sloveGuass();

77     printAns();

78     return 0;

79 }
View Code

[JSOI2008]星球大战starwar

  进击的水题= =离线倒过来搞就是裸的并差集了

 [JSOI2008]最小生成树计数

  终于不算水题了。。可以发现一个性质:最小生成树中某种边权的边的数量是固定的,有注意到相同边权的边不超过10条,所以先求最小生成树然后2^10枚举子集,判断即可。

 [JSOI2009]游戏Game

  在图上的博弈问题。突然就想起了NOI那道[兔兔和蛋蛋的游戏],然后就明白了。都是一个模型,本质上是二分图增广路的性质。

  简单来说,如果先手放在一个【一定在最大匹配内】的点上,那么必输。为什么呢?因为这是后手可以移动到其对应的匹配点上。如果先手继续移动到匹配点上,那么后手可以沿着最大匹配的交错轨移动,所以只要先手移动,后手一定能移动,而且最后是先手不能移动(因为如果先手又能移动了,那么就得到了一条增广路,那么互换匹配边和非匹配变后最大匹配+1,因为最大匹配已经求出来了,所以显然不存在增广路,所以最后先手不能继续移动到匹配点)。如果先手移动到了未匹配点上,那么后手可以继续移动到一个匹配点,就和刚才一样了。所以可知,先手放在一个【一定在最大匹配内】的点上,那么必输。

  同理,如果先手放在一个【不一定在最大匹配内】的点上,那么就必胜了。因为这时后手不得不移动到非匹配点,如同上面的过程,就是先手占了优势,所以先手必胜。

  那么问题就简单了。把网格黑白染色,求最大匹配。

  如何判断一个点是否一定在最大匹配上呢?对于X部,从S开始沿残量网络DFS,不能走到的点就一定在最大匹配上。同理对于Y部把原图反向,从T开始DFS。

  这又是为什么呢?(= =) 还是考虑增广路,DFS的过程实际上得到了一条交错轨,那么互换匹配边与非匹配边后匹配点一定不变,因为变了就出现了增广路。所以这些点在任意一种最大匹配中都是匹配点。

  那么就完事了。

  做这道题的时候不幸发现 我的好友【智商】已经下线,所以代码就不贴了。。。反正是写了200+行的巨挫代码。

[JSOI2009]Count

  果然我还是就会做水题T_T。每种颜色开一个BIT,单点修改区间和查询。

/**************************************************************

    Problem: 1452

    User: wsc500

    Language: C++

    Result: Accepted

    Time:4516 ms

    Memory:42940 kb

****************************************************************/

 

#include <cstdio>

#include <cstdlib>

#include <iostream>

#include <cstring>

#include <algorithm>

#include <queue>

#include <vector>

 

using namespace std;

#define MAXN (300+10)

#define MAXC (100+10)

#define lowbit(i) (i&(-i))

 

/*Gloable*/

int data[MAXN][MAXN];

int m,n;

/*Function*/

struct BIT{

    int t[MAXN][MAXN];

    void add(int x,int y,int val){

        for (int i=x;i<=n;i+=lowbit(i))

            for (int j=y;j<=m;j+=lowbit(j))

                t[i][j]+=val;

    }

    int _query(int x,int y){

        int rtn=0;

        for (int i=x;i>=1;i-=lowbit(i)){

            for (int j=y;j>=1;j-=lowbit(j)){

                rtn+=t[i][j];

            }

        }

        return rtn;

    }

    int query(int xa,int ya,int xb,int yb){

        return _query(xb,yb)-_query(xb,ya-1)-_query(xa-1,yb)+_query(xa-1,ya-1);

    }

}B[MAXC];

 

void readData(){

    scanf("%d%d",&n,&m);

    for (int i=1;i<=n;i++){

        for (int j=1;j<=m;j++){

            scanf("%d",&data[i][j]);

        }

    }

}

void init(){

    for (int i=1;i<=n;i++){

        for (int j=1;j<=m;j++){

            B[data[i][j]].add(i,j,1);

        }

    }

}

void work(){

    int q,cmd,a,b,c,d,e;

    scanf("%d",&q);

    for (int i=1;i<=q;i++){

        scanf("%d",&cmd);

        if (cmd==1){

            scanf("%d%d%d",&a,&b,&c);

            B[data[a][b]].add(a,b,-1);

            B[c].add(a,b,1);

            data[a][b]=c;

        }

        if (cmd==2){

            scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);

            printf("%d\n",B[e].query(a,c,b,d));

        }

    }

}

int main()

{

    readData();

    init();

    work();

    return 0;

}
View Code

[JSOI2009]去括号

  这是以前做的那道。好像直接乱搞就可以了?忘了。代码就不贴了

[JSOI2010]Group 部落划分 Group

  可以二分答案,然后统计块数。也可以直接给所有点对按距离从小到大排序,逐个合并直到块数<k。

 1 /**************************************************************

 2     Problem: 1821

 3     User: wsc500

 4     Language: C++

 5     Result: Accepted

 6     Time:488 ms

 7     Memory:17236 kb

 8 ****************************************************************/

 9  

10 #include <cstdio>

11 #include <cstdlib>

12 #include <iostream>

13 #include <cstring>

14 #include <algorithm>

15 #include <queue>

16 #include <stack>

17 #include <vector>

18 #include <cmath>

19  

20 using namespace std;

21 #define MAXN (1000+10)

22 #define minner(a,b) ((a)<(b)?(a):(b))

23  

24 /*Gloable*/

25 struct pos{

26     double x,y;

27     pos(double x=0.0,double y=0.0):x(x),y(y) {}

28 };

29 typedef struct pos pos;

30 pos data[MAXN];

31 int n,k,L;

32 pair<double,pair<int,int> > lst[MAXN*MAXN];

33 int fa[MAXN],blocks;

34 /*Function*/

35 void init(){

36     for (int i=0;i<=MAXN;i++)   fa[i]=i;

37     blocks=n;

38 }

39 int find(int x){

40     int a=x,temp;

41     while (x!=fa[x])    x=fa[x];

42     while (a!=fa[a])    temp=fa[a] , fa[a]=x , a=temp;

43     return x;

44 }

45 void join(int x,int y){

46     int fx=find(x),fy=find(y);

47     if (fx!=fy) fa[fx]=fy , blocks--;

48 }

49 inline double getDis(pos a,pos b){

50     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));

51 }

52 void readData(){

53     double a,b;

54     scanf("%d%d",&n,&k);

55     for (int i=1;i<=n;i++){

56         scanf("%lf%lf",&a,&b);

57         data[i]=pos(a,b);

58     }

59 }

60 void work(){

61     for (int i=1;i<=n;i++)

62         for (int j=1;j<=n;j++)

63             if (i!=j)   lst[L++]=make_pair(getDis(data[i],data[j]),make_pair(i,j));

64     sort(lst,lst+L);

65     if (blocks<k)  {printf("%.2lf\n",lst[0].first);return;}

66     for (int i=0;i<L;i++){

67         join(lst[i].second.first,lst[i].second.second);

68         if (blocks<k)  {printf("%.2lf\n",lst[i+1].first);break;}

69     }

70 }

71 int main()

72 {

73     readData();

74     init();

75     work();

76     return 0;

77 }
View Code

[Jsoi2010]连通数

  直接缩环这很明显了吧。刚开始以为按拓扑序像DP一样更新就行了,但发现有重复计算。那么就对每个点染色或者求最短路,反正是DAG怎么搞都是O(n)。然后对于每个可达的块把点数乘起来最后累加就行了。总复杂度n^2。网上有人说n^2超时了,那肯定是写搓了。还有那个n^3压位是怎么来的?说是O(n)我还信。。。反正这种方法好写好调。1A。

  UPD:我又想了一下,好像DP是可以的。。压一下状态,记录都是从那几个点过来的,然后按拓扑序转移状态就行了。。这是不是网上说的那种方法?但绝对不是n^3。

  1 /**************************************************************

  2     Problem: 2208

  3     User: wsc500

  4     Language: C++

  5     Result: Accepted

  6     Time:8072 ms

  7     Memory:63836 kb

  8 ****************************************************************/

  9  

 10 #include <cstdio>

 11 #include <cstdlib>

 12 #include <iostream>

 13 #include <cstring>

 14 #include <algorithm>

 15 #include <queue>

 16 #include <stack>

 17 #include <vector>

 18  

 19 using namespace std;

 20 #define MAXN (2000+10)

 21 #define MAXM (2000*2000+10)

 22 #define minner(a,b) ((a)<(b)?(a):(b))

 23  

 24 /*Debug*/

 25  

 26 #define debug

 27  

 28 /*Gloable*/

 29 struct Graph{

 30     int head[MAXN],next[MAXM],e[MAXM],countside;

 31     void buildside(int a,int b){

 32         e[countside]=b;

 33         next[countside]=head[a];

 34         head[a]=countside++;

 35     }

 36     Graph(){

 37         memset(head,0,sizeof(head));

 38         memset(next,0,sizeof(next));

 39         memset(e,0,sizeof(e));

 40         countside=1;

 41     }

 42 }G1,G2;

 43 int n;

 44 int dfs_clock,SCC_id;// for tarjan()

 45 int id[MAXN],lowlink[MAXN],mark[MAXN];// for tarjan()

 46 bool inStack[MAXN];// for tarjan()

 47 stack<int> S; // for tarjan()

 48 bool v[MAXN];// for color()

 49 int ct[MAXN],ans[MAXN];// for ans;

 50 /*Function*/

 51 void tarjan_dfs(int p){

 52     id[p]=lowlink[p]=dfs_clock++;

 53     S.push(p);

 54     inStack[p]=true;

 55     for (int i=G1.head[p];i>0;i=G1.next[i]){

 56         if (id[G1.e[i]]==-1){

 57             tarjan_dfs(G1.e[i]);

 58             lowlink[p]=minner(lowlink[p],lowlink[G1.e[i]]);

 59         }else if(inStack[G1.e[i]]){

 60             lowlink[p]=minner(lowlink[p],id[G1.e[i]]);

 61         }

 62     }

 63     if (lowlink[p]==id[p]){

 64         while (1){

 65             int now=S.top();S.pop();

 66             inStack[now]=false;

 67             mark[now]=SCC_id;

 68             if (now==p) break;

 69         }

 70         SCC_id++;

 71     }

 72 }

 73 void tarjan(){

 74     for (int i=1;i<=n;i++)  id[i]=-1;

 75     memset(inStack,false,sizeof(inStack));

 76     dfs_clock=1;

 77     SCC_id=1;

 78     for (int i=1;i<=n;i++)  if (id[i]==-1)    tarjan_dfs(i);

 79  

 80     for (int i=1;i<=n;i++)  ct[mark[i]]++;

 81 }

 82 void readData(){

 83     char str[MAXN];

 84     scanf("%d",&n);

 85     for (int i=1;i<=n;i++){

 86         scanf("%s",str);

 87         for (int j=0;j<n;j++)   if (str[j]=='1')   G1.buildside(i,j+1);

 88     }

 89 }

 90 void rebuild(){

 91     for (int i=1;i<=n;i++){

 92         for (int j=G1.head[i];j>0;j=G1.next[j])

 93                 if (mark[i]!=mark[G1.e[j]]) {

 94                         G2.buildside(mark[i],mark[G1.e[j]]);

 95                     //cout<<mark[i]<<" "<<mark[G1.e[j]]<<endl;

 96                 }

 97     }

 98     n=SCC_id-1;

 99 }

100 void color(int p){

101     queue<int> q;

102     q.push(p);

103     //v[p]=true;

104     while (!q.empty()){

105         int now=q.front();q.pop();

106         for (int i=G2.head[now];i>0;i=G2.next[i])

107             if (!v[G2.e[i]]) v[G2.e[i]]=true , q.push(G2.e[i]);

108     }

109 }

110 void work(){

111     for (int i=1;i<=n;i++){

112         memset(v,false,sizeof(v));

113         color(i);

114         for (int j=1;j<=n;j++)  if (v[j])

115             ans[j]+=ct[i]*ct[j];

116     }

117 }

118 void printAns(){

119     int result=0;

120     for (int i=1;i<=n;i++)  result+=ans[i] , result+=ct[i]*(ct[i]);

121     printf("%d\n",result);

122 }

123 int main()

124 {

125     readData();

126     tarjan();

127     rebuild();

128     work();

129     printAns();

130     return 0;

131 }
View Code

 [Jsoi2009]瓶子和燃料

  首先那个倒水问题的最小答案是瓶子容量们的最大公约数。原理等同于辗转相减(还是叫更相减损?)。那么问题就转化成了给出n个数,选出k个是他们的GCD最大。进一步转化就是求一个最大的数使他是至少k个数的约数。那么就把每个数因式分解(注意不是质因数分解),然后找到出现次数>=k且大小最大的因数就行了,因为肯定有解,所以直接按出现次数和数字大小排一下序就行了。

 1 /**************************************************************

 2     Problem: 2257

 3     User: wsc500

 4     Language: C++

 5     Result: Accepted

 6     Time:2244 ms

 7     Memory:7132 kb

 8 ****************************************************************/

 9  

10 #include <cstdio>

11 #include <cstdlib>

12 #include <iostream>

13 #include <cstring>

14 #include <algorithm>

15 #include <cmath>

16  

17 using namespace std;

18 #define MAXN (1000001+10)

19 #define MAXF (500000)

20  

21 /*Gloable*/

22 int lst[MAXF],v[MAXN],s;

23 int n,k;

24 /*Function*/

25 void readData(){

26     scanf("%d%d",&n,&k);

27     for (int i=1;i<=n;i++)  scanf("%d",&v[i]);

28 }

29 void getPrimeFact(int x){

30     int i;

31     for (i=1;i*i<x;i++){

32         if (x%i==0) lst[s++]=i , lst[s++]=x/i;

33         //while (x%i!=0)  x/=i;

34     }

35     if (i*i==x) if (x%i==0) lst[s++]=i;

36 }

37 void work(){

38     for (int i=1;i<=n;i++)

39         getPrimeFact(v[i]);

40     sort(lst,lst+s);

41     int st=0,mx=0;

42     lst[s]=-1;

43     for (int i=0;i<s;i++){

44         if (lst[i+1]!=lst[i]){

45             if (i+1-st>=k&&lst[i]>lst[mx])   mx=i;

46             st=i+1;

47         }

48     }

49     printf("%d\n",lst[mx]);

50 }

51 int main()

52 {

53     readData();

54     work();

55     return 0;

56 }
View Code

 

你可能感兴趣的:(js)