• 【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
    ",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
    ");
    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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",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
    ",lst[mx]);
    50 }
    51 int main()
    52 {
    53     readData();
    54     work();
    55     return 0;
    56 }
    View Code
  • 相关阅读:
    bat学习
    Eclipse调试方法及快捷键
    JDK中的设计模式
    开源-自由-乔布斯
    AOP
    编程语言
    [LeetCode] 160. Intersection of Two Linked Lists(两个单链表的交集)
    [LeetCode] 198. House Robber(小偷)
    [LeetCode] 155. Min Stack(最小栈)
    [LeetCode] 1. Two Sum(两数之和)
  • 原文地址:https://www.cnblogs.com/loveidea/p/3153863.html
Copyright © 2020-2023  润新知