• 屯题大法好


    CF 535C Tavas and Karafs

    题目大意:给你一个无限长的等差数列,每次给一个起点L,可以吃T轮,每可以把M个数吃一口(-1),问最大的R使得区间[L,R]被吃完

    思路:显然给定一个区间[L,R]后很容易贪心出能不能被吃完,并且发现该性质有单调性也就是如果[L,R]可以吃完,那[L,R-1]也可以,且存在最大的R使得[L,R+1]不满足条件,于是二分一下就可以了

     1 #include<iostream>
     2 #include<cstdio>
     3 #define ll long long
     4 using namespace std;
     5 ll a,b,n,l,t,m,rr,lr;
     6 int check(ll k)
     7 {
     8     ll f = a + (l - 1) * b,rrr = a + (k - 1) * b,su=((f+rrr)*(k-l+1))>>1;
     9     if(t*m<su || rrr > t)return 0;
    10     else return 1;
    11 }
    12 int main()
    13 {
    14     cin>>a>>b>>n;
    15     for(int i=1;i<=n;i++)
    16     {
    17         cin>>l>>t>>m;
    18         if(a+b*(l-1) > t)
    19         {
    20             puts("-1");
    21             continue;
    22         }
    23         lr = l;
    24         rr = l + t+10;
    25         while(lr<rr)
    26         {
    27             int mid = (lr + rr+ 1) >> 1;
    28             if(check(mid))lr=mid;else rr=mid-1;
    29         }
    30         cout<<lr<<endl;
    31     }
    32     return 0;
    33 }
    View Code

    CF 533B. Work Group

    题目大意:给出一棵有根树,每个点都有点权,从中选出一些点,使得每个选中的点的子树中都有偶数个选中点,求选出点最大的点权
    思路:很明显的树dp,显然选中当前点的话,那它的子数选中的点只能选择偶数,如果不选当前的点,子树可以选偶数或奇数个点,那接下来问题就转化成了对于一个点,它的子树可以选奇数个点或偶数的点,以及一个权值,如何选才能使权值最大并且点数为奇数/偶数。这个显然是一个一维的DP,于是问题得到解决
    唔,细节处理还是很重要的,由于一开始什么0个点都没选的时候点数是偶数,因此需要一开始将奇数转移的地方封印起来,直到选了奇数个后才能用odd转移

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define maxn 400000
     5 using namespace std;
     6 long long now=0,head[maxn],next[maxn],point[maxn],p[maxn];
     7 long long dp[400000][3],root;
     8 void add(int x,int y)
     9 {
    10     next[++now]=head[x];
    11     head[x]=now;
    12     point[now]=y;
    13 }
    14 long long dfs(long long k,long long par)
    15 {
    16     if(dp[k][par]!=-1)return dp[k][par];
    17     long long even = 0 ,odd = -1;
    18     for(int i=head[k];i;i=next[i])
    19     {
    20         long long pp = point[i],u=dfs(pp,0) , v = dfs(pp,1);
    21         long long tempe = even , tempo = odd;
    22         even += u;odd += u;
    23         if(v != -1)
    24         {
    25             if(tempo != -1)even = max(even , tempo + v);
    26             odd = max(odd , tempe +v);
    27         }
    28     }
    29     dp[k][0]=even;
    30     dp[k][1] = max(even + p[k], odd);
    31     return dp[k][par];
    32 }
    33 int main()
    34 {
    35     long long n,x;
    36     memset(dp,-1,sizeof(dp));
    37     cin>>n;
    38     for(int i=1;i<=n;i++)
    39     {
    40         cin>>x>>p[i];
    41         if(x==-1)root=i;
    42         else
    43         {
    44             add(x,i);
    45         }
    46     }
    47     cout<<max(dfs(root,0),dfs(root,1))<<endl;
    48     return 0;
    49 }
    View Code

    GCJ 2015 A. Counter Culture

    题目大意:给你一个数,从0出发每次可以对这个数进行两个操作:1.将这个数反转:也就是1234变成4321 2.将这个数加一

    问从0出发最少多少次操作能变成这个数

    思路:这题做的,快苦了。。。首先20一下只能一步一步加,以上呢?先凑出来到这个位数至少需要多少步,比如到达100需要多少步,1000需要多少步,然后将末尾一半的数凑成高位,反转,再将后一半的数凑成高位就能过大数据了,可惜的是闹抽两句话写反了结果fst!!!!

     1 #include<iostream>
     2 #include<fstream>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define maxn 500
     6 using namespace std;
     7 ifstream fin("a.in");
     8 ofstream fout("a.out");
     9 //#define fin cin
    10 //#define fout cout
    11 int deg[100]={0,1,10,29,138,337,1436,3435,14434,34433,144432,344431,1444430,3444429,14444428};
    12 int main()
    13 {
    14     long long t,n,cas=0;
    15     fin>>t;
    16     while(t--)
    17     {
    18         long long ans=0;
    19         fin>>n;
    20         int flag=0;
    21         if(n<=20)
    22         {
    23             fout<<"Case #"<<++cas<<": "<<n<<endl;
    24         }
    25         else
    26         {
    27             long long temp=n;
    28             if(n%10==0)
    29             {
    30                 n--;
    31                 flag=1;
    32             }
    33             int a[100],h=0;
    34             while(n)
    35             {
    36                 a[++h]=n%10;
    37                 n/=10;
    38             }
    39             int u=h>>1;
    40             ans += deg[h];
    41             long long v=0;
    42             for(int i=h-u+1;i<=h;i++)
    43             {
    44                 v = v * 10 + a[i];
    45             }
    46             ans  += v;
    47             v=0;
    48             for(int i=h-u;i>=1;i--)
    49             {
    50                 v= v*10 +a[i];
    51             }
    52             ans+=v;
    53             v=1;
    54             for(int i=1;i<h;i++)v*=10;
    55             if(flag)ans++;
    56             fout<<"Case #"<<++cas<<": "<<min(ans,temp-v+deg[h])<<endl;
    57         }
    58     }
    59     return 0;
    60 }
    View Code

    Codeforces Round #300 B. Quasi Binary

    题目大意:给出一个数(0到1e6),问你它能是最少多少quasibinary的和,一个十进制数是quasibinary当且仅当它十进制数位上只有0和1

    思路:显然这个范围内quasibinary不多,先预处理出来,然后就是一个裸到不行的DP,记录路径就可以

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define maxn 2000000
     6 using namespace std;
     7 int a[maxn],h=0,ini[maxn],dp[maxn],last[maxn],ans[maxn];
     8 void add(int temp[])
     9 {
    10     int sum=0;
    11     for(int i=1;i<=7;i++)sum = sum*10 + temp[i];
    12     ini[++h]=sum;
    13 }
    14 void dfs(int k)
    15 {
    16     if(k>7){add(a);return ;}
    17     a[k]=1;dfs(k+1);
    18     a[k]=0;dfs(k+1);
    19 }
    20 int dfs2(int k)
    21 {
    22     if(dp[k]!=-1)return dp[k];
    23     int ret=0x3f3f3f3f;
    24     for(int i=1;i<=h;i++)
    25     {
    26         if(k-ini[i]>=0)
    27         {
    28             int u=dfs2(k-ini[i]);
    29             if(u < ret)
    30             {
    31                 ret = u;
    32                 last[k] = i;
    33             }
    34         }
    35         else break;
    36     }
    37     return dp[k]=ret+1;
    38 }
    39 int main()
    40 {
    41     int n;
    42     scanf("%d",&n);
    43     dfs(1);
    44     memset(dp,-1,sizeof(dp));
    45     dp[0]=0;
    46     h--;
    47     sort(ini+1,ini+1+h);
    48     printf("%d
    ",dfs2(n));
    49     int v=0;
    50     while(last[n]!=0)
    51     {
    52         ans[++v]=ini[last[n]];
    53         n-=ini[last[n]];
    54     }
    55     sort(ans+1,ans+1+v);
    56     for(int i=1;i<=v;i++)printf("%d ",ans[i]);
    57     return 0;
    58 }
    View Code

    Codeforces Round #298 (Div. 2)  F. Simplified Nonogram

    题目大意:有一个N×M的网格(n<=5 m<=20)每个格子可能黑可能白,告诉你每行没列有多少段连续的黑格(比如0是黑格1是白格 1100110101等于3)让你给出一种可行的棋盘方案

    思路:先想朴素的办法,发现N比较小,于是可以枚举每列的状态,dfs更新行的状态即可,这样妥妥的超时

    然后想办法记录,发现到第I列的时候,如果此时每行的状态是一样的(也就是到这一列的时候每行有多少连续的黑格子)那么两种状态是一样一样的,因此如果之前访问过了这种状态下次再访问到,就可以PASS了,这样剪枝了以后就可以了,注意压常数,一开始用vector在109组数据无情的挂了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<vector>
     5 #define maxn 35
     6 using namespace std;
     7 int a[6],b[maxn],c[maxn],n,m,g[11][50];
     8 int ans[maxn][maxn],next[maxn];
     9 bool visit[21][11][11][11][11][11][33];
    10 bool dfs(int k,int last)
    11 {
    12     if(k>m)
    13     {
    14         for(int i=1;i<=n;i++)if(a[i]!=0)return false;
    15         return true;
    16     }
    17     int temp[6];
    18     if(visit[k][a[1]][a[2]][a[3]][a[4]][a[5]][last])return false;
    19     for(int i=1;i<=g[b[k]][0];i++)
    20     {
    21         int u=g[b[k]][i],flag2=0;
    22         memcpy(temp,a,sizeof(a));
    23         for(int j=0;j<n;j++)
    24         {
    25             if( (u&(1<<j))!=0 && (last& (1<<j))==0 )
    26             {
    27                 a[j+1]--;
    28                 if(a[j+1]<0){flag2=1;break;}
    29             }
    30         }
    31         if(flag2)
    32         {
    33             memcpy(a,temp,sizeof(a));
    34             continue;
    35         }
    36         if(dfs(k+1,u))
    37         {
    38             next[k]=u;
    39             return true;
    40         }
    41         memcpy(a,temp,sizeof(a));
    42     }
    43     visit[k][a[1]][a[2]][a[3]][a[4]][a[5]][last]=1;
    44     return false;
    45 }
    46 int main()
    47 {
    48     scanf("%d%d",&n,&m);
    49     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    50     for(int i=1;i<=m;i++)scanf("%d",&b[i]);
    51     for(int s=0;s<=((1<<n)-1);s++)
    52     {
    53         int v=0;
    54         for(int i=1;i<=n;i++)
    55         {
    56             if(((s>>(i-1)) & 3) == 1)v++;
    57         }
    58         g[v][++g[v][0]]=s;
    59     }
    60     dfs(1,0);
    61     for(int i=1;i<=m;i++)
    62         for(int j=1;j<=n;j++)
    63         {
    64             if(next[i]&(1<<(j-1)))ans[j][i]=1;else ans[j][i]=0;
    65         }
    66     for(int i=1;i<=n;i++)
    67         for(int j=1;j<=m;j++)
    68         {
    69             if(ans[i][j])printf("*");else printf(".");
    70         }
    71         puts("");
    72     return 0;
    73 }
    View Code

     Codeforces Round #290 (Div. 2) D. Fox And Jumping

    题目大意:给定N个数,有选它价值和能跳的步数,问要能跳到任意一个点所花费的最小价值

    思路:显然如果有一种方案能拼到1,那么区间上所有的点都能跳了,假设选择lp,lq,lr...ls能凑成1,也就是lp*x+lq*y+...+ls*z=1;方程有整数解当且仅当(lp,lq,...ls)=1,因此dp[i]表示凑成gcd[i]最小的花费是多少,然后就可以转移了,发现gcd是递减的,所以能开一个队列乱搞

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<map>
     5 #include<queue>
     6 #define maxn 1000
     7 using namespace std;
     8 map<int,int>h;
     9 queue<int>q;
    10 int l[maxn],c[maxn];
    11 int main()
    12 {
    13     int n;
    14     scanf("%d",&n);
    15     for(int i=1;i<=n;i++)scanf("%d",&l[i]);
    16     for(int i=1;i<=n;i++)
    17     {
    18         scanf("%d",&c[i]);
    19         if(h[l[i]]==0)
    20         {
    21             q.push(l[i]);
    22             h[l[i]]=c[i];
    23         }
    24         else
    25         {
    26             h[l[i]]=min(h[l[i]],c[i]);
    27         }
    28     }
    29     while(!q.empty())
    30     {
    31         int u=q.front();
    32         q.pop();
    33         for(int i=1;i<=n;i++)
    34         {
    35             int v=__gcd(l[i],u),w=h[u]+h[l[i]];
    36             if(h[v]>w || h[v]==0)
    37             {
    38                 h[v]=w;
    39                 q.push(v);
    40             }
    41         }
    42     }
    43     if(h[1]==0)
    44     {
    45         puts("-1");
    46     }
    47     else printf("%d
    ",h[1]);
    48     return 0;
    49 }
    View Code

     Codeforces Round #300 E. Demiurges Play Again

    题目大意:两个无聊的人在看两个无聊的人玩一个游戏,有一棵树,假设有M个叶子节点,每个叶子节点都有一个1到M的权值,从根节点开始,两人轮流操作,每轮操作将当前点移动到它的一个儿子上,先手的目的是使得最后到叶子节点的权值尽量大,,直到移动到叶子节点不能动为止,两个人毋庸置疑都采取最优的操作,两个无聊的人可以预先将叶子节点的权值重排,问重排以后能让分数最大是多少,最小多少?

    思路:假设权值全部排好了,那么两个人玩游戏得到的分数也就确定了(他们都是聪明人),只要用树DP第一个人走的时候取子树能取到的最大值,第二个人取最小值,那假设没排好,dp[i][1]表示以i为根的子树先手先走能取到第K大的数(因为分数已经确定了,所以对于一个子树能取到第K大也就确定了),dp[i][0]表示以i为根的子树后手先走能取到第K大的数,然后先手是聪明人,一定取名次最小的那个子树,那我们顺水人情,把最大值放到这个子树就行了,如果后手走,肯定取名次最大的,容易发现最后取到的就是所有子树名词的和,然后就可以DP了

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define maxn 400009
     5 using namespace std;
     6 int next[maxn],head[maxn],point[maxn],now=0,leaf;
     7 bool visit[maxn];
     8 void add(int x,int y)
     9 {
    10     next[++now]=head[x];
    11     head[x]=now;
    12     point[now]=y;
    13 }
    14 int dfs(int k,bool f)
    15 {
    16     visit[k]=1;
    17     int minx=0x3f3f3f3f,ret=0,flag=1;
    18     for(int i=head[k];i;i=next[i])if(!visit[point[i]])
    19     {
    20         flag=0;
    21         int u=point[i];
    22         if(f)
    23         {
    24             minx=min(minx,dfs(u,f^1));
    25         }
    26         else
    27         {
    28             ret+=dfs(u,f^1);
    29         }
    30     }
    31     if(flag)
    32     {
    33         leaf++;
    34         return 1;
    35     }
    36     if(f)return minx;else return ret;
    37 }
    38 int main()
    39 {
    40     int n,x,y;
    41     scanf("%d",&n);
    42     for(int i=1;i<n;i++)
    43     {
    44         scanf("%d%d",&x,&y);
    45         add(x,y);
    46         add(y,x);
    47     }
    48     int u=dfs(1,1);
    49     leaf=0;
    50     memset(visit,0,sizeof(visit));
    51     int v = dfs(1,0);
    52     printf("%d %d
    ",leaf-u+1,v);
    53     return 0;
    54 }
    View Code

    Codeforces Round #301 (Div. 2)  D. Bad Luck Island

    题目大意:一个岛上有三种人剪刀人石头人和布人,每次任意两个人相遇,相遇后如果不同种人按剪刀石头布的规则一个人把另一个人消灭,问最后只剩下剪刀人,只剩下石头人,只剩下布人的概率

    思路:dp[r][s][p]表示三类人分别有r s p个人 ,最后只剩下r 的概率 ,(其中r能消灭s s能消灭p p能消灭r)

    然后容易发现两个人的种类相同是无所谓的,所以两个人遇到不同的总数是r*s+r*p+s*p,然后就可以根据剪刀石头布的规则转移了

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 using namespace std;
     5 double dp[109][109][109];
     6 bool visit[109][109][109];
     7 double dfs(int r,int s,int p)
     8 {
     9     if(r==0)return 0.0;
    10     if(s==0 && p==0)return 1.0;
    11     if(visit[r][s][p])return dp[r][s][p];
    12     visit[r][s][p]=1;
    13     int tot = r*s + r*p + s*p;
    14     dp[r][s][p]=0;
    15     if(s)dp[r][s][p]+=1.0*dfs(r,s-1,p) * (1.0 * r * s) / (1.0*tot);
    16     if(r)dp[r][s][p]+=1.0*dfs(r-1,s,p) * (1.0 * r * p) / (1.0*tot);
    17     if(p)dp[r][s][p]+=1.0*dfs(r,s,p-1) * (1.0 * s * p) / (1.0*tot);
    18     return dp[r][s][p];
    19 }
    20 int main()
    21 {
    22     int r,s,p;
    23     scanf("%d%d%d",&r,&s,&p);
    24     printf("%.12f %.12f %.12f
    ",dfs(r,s,p),dfs(s,p,r),dfs(p,r,s));
    25     return 0;
    26 }
    View Code

    Codeforces Round #296 (Div. 2)  D. Clique Problem

    题目大意:在数轴上有N个点,每个点有一个权重,两个点之间有边当且仅当它们的权重之和小于等于它们的距离,问这个图的最大团的数量

    思路:好神的题,显然||xi- xj| <=  wi +wj可以变成

    xi- xj <=  wi +wj ||  xi- xj <=  wi +wj 

    其中两个不等式最后都可以化简成 xi - wi <=  xj +wj

    所以对一个点只需要记录Xi+ Wi 和Xi- Wi的信息就可以 

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #define maxn 200000
     5 #define inf 0x3f3f3f3f
     6 using namespace std;
     7 struct T
     8 {
     9     int x;
    10     int y;
    11 }a[maxn];
    12 int cmp(T x,T y)
    13 {
    14     return x.x < y.x;
    15 }
    16 int x,w;
    17 int
    18 main()
    19 {
    20     int n;
    21     scanf("%d",&n);
    22     for(int i=1;i<=n;i++)
    23     {
    24         scanf("%d%d",&x,&w);
    25         a[i].x = x + w;
    26         a[i].y = x - w;
    27     }
    28     sort(a+1, a+1+n,cmp);
    29     int maxx = -inf,ans = 0;
    30     for(int i=1;i<=n;i++)
    31     {
    32         if(a[i].y >= maxx)
    33         {
    34             maxx = a[i].x;
    35             ans++;
    36         }
    37     }
    38     printf ("%d
    ",ans);
    39     return 0;
    40 }
    View Code
  • 相关阅读:
    一个老码农对职场的思考
    PHP算法 (冒泡 选择排序 折半查找 顺序查找 斐波那契数列 素数 )
    php 图像的基本操作
    php 随机字符串 验证码
    php中 var_dump 和 echo 和 print_r() 和 print
    正则表达式的整理
    php上传文件原理
    git 几个简单命令
    git 几个简单命令
    会话技术Cookie
  • 原文地址:https://www.cnblogs.com/philippica/p/4446022.html
Copyright © 2020-2023  润新知