codeforces 187 (div2)题解


http://codeforces.com/problemset/problem/315/A


这两天做了两场CF的题(div2),没事补个题解吧。

A题:题目很水,但是有些恶心的trick,比如有的罐子可以打开没有出现的编号的罐子,还有自己不能打开自己,题目没看清,WA了一堆,最后直接O(n^2)暴力水过。。。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define maxn 100010
using namespace std;
int a[110],b[110],num[110];
int main()
{
    //freopen("dd.txt","r",stdin);
    int n,i;
    scanf("%d",&n);
    int sum=0;
    for(i=0;i<n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
    }
    for(i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(i!=j)
            {
                if(b[i]==a[j])
                {
                    num[j]=1;
                }
            }
        }
    }
    for(i=0;i<n;i++)
    sum+=num[i];
    printf("%d\n",n-sum);
    return 0;
}


http://codeforces.com/problemset/problem/315/B

B题:初看题意以为要用线段树之类的数据结构解决,其实不用,因为操作2是对所有元素的操作,我们只需要维护一个值sum记录到目前为止操作2的累加和即可,同时在做操作1时,假如要把一个数赋值为x,我们只要将它调整为赋值x-sum即可,对于操作三的询问,输出a[qi]+sum即为所求。


#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 100010
#define ll long long
using namespace std;
ll a[maxn];
int main()
{
    //freopen("dd.txt","r",stdin);
    int n,i,m;
    ll sum=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    scanf("%I64d",&a[i]);
    while(m--)
    {
        int t,x,y;
        scanf("%d",&t);
        {
            if(t==1)
            {
                scanf("%d%d",&x,&y);
                a[x]=y-sum;
            }
            else if(t==2)
            {
                scanf("%d",&x);
                sum+=x;
            }
            else
            {
                scanf("%d",&x);
                printf("%I64d\n",sum+a[x]);
            }
        }
    }
    return 0;
}


http://codeforces.com/problemset/problem/315/C
C题:题目有些长,但是看了样例还是很容易明白的,因为di的计算方法只和i前面的人还有人的总数n有关,且我们去掉人的顺序也是从小到大,所以我们可以从左往右一个一个判定a[i]是否应该被去掉,在判断的同时我们维护两个值,po表示当前待判断的人前面有多少人(没有被去掉),nn表示当前还剩多少人。那么对于每一个人计算di,若需要被去掉,则nn--,否则po++,如何快速计算di还是比较好想的,可以用类似dp的方法解决,具体实现请参考代码。



#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <assert.h>
#define maxn 200010
#define ll long long
using namespace std;
ll a[maxn];
int vis[maxn];
int main()
{
   //freopen("dd.txt","r",stdin);
   int n,k;
   scanf("%d%d",&n,&k);
   int i,nn=n,po=1;//nn表示还剩几人,po表示当前确定留下的人的数量
   ll tmp=0,d=0;
   scanf("%I64d",&a[1]);
   for(i=2;i<=n;i++)
   {
       scanf("%I64d",&a[i]);
       d=tmp-po*a[i]*(nn-po-1);//计算di
       if(d<k)//说明去掉一个
       {
           vis[i]=1;
           nn--;
       }
       else
       {
           tmp+=po*a[i];
           po++;
       }

   }
   for(i=1;i<=n;i++)
   if(vis[i])
   printf("%d\n",i);
    return 0;
}

http://codeforces.com/problemset/problem/315/D

D题:首先很容易知道,若 [a, b] 串最多能obtain(具体含义见题目描述)x个 [c, d]串,则答案就为x。进一步,设[a,b]最多能obtain y个c串,则x=y/d。·求y的过程类似于求循环节,设num[i]表示[a,i]能obtain多少个c串,且设l2[i]表示[a,i]匹配完后最后一个匹配到c串的第几个字符,然后求循环节即可,具体实现还需要些细节,不嫌代码丑的话可以参考代码:


#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 100010
using namespace std;
char s1[110],s2[110];
int num[10100],vis[110];
int main()
{
    //freopen("dd.txt","r",stdin);
    int b,d;
    scanf("%d%d",&b,&d);
    scanf("%s%s",s1,s2);
    int len1=strlen(s1),len2=strlen(s2);
    int t=0,l1=0,l2=0,sum=0,old=0;
    num[0]=0;
    int v[26];
    memset(v,0,sizeof(v));
    for(int i=0;i<len1;i++)
    v[s1[i]-'a']++;
    for(int i=0;i<len2;i++)
    {
        if(!v[s2[i]-'a'])
        {
            printf("0\n");
            return 0;
        }
    }
    memset(vis,-1,sizeof(vis));
    vis[0]=0;
    while(1)
    {
        if(s1[l1]==s2[l2])
        {
            l1++;
            l2++;
        }
        else
        l1++;
        if(l2==len2)
        {
            sum++;
            l2=0;
        }
        if(l1>len1)
        {
            t++;
            num[t]=sum;
            l1=0;
            if(vis[l2]!=-1)
            {
                old=vis[l2];
                break;
            }
            vis[l2]=t;
        }
    }
    int tmp=0;
    if(b<=t)
    {
        tmp=num[b];
    }
    else
    {
        tmp=num[t]+(num[t]-num[old])*((b-t)/(t-old));
        b-=t;
        b%=(t-old);
        tmp+=num[old+b]-num[old];
    }
    printf("%d\n",tmp/d);
    return 0;
}


http://codeforces.com/problemset/problem/315/E
E题:很水的dp,易知我们要求的就是a串的所有不同的非下降子序列中,各位数乘积的和,我们设dp[i]表示以i结尾的非下降子序列的答案,我们从左到右一步一步更新dp值,转移方程很简单,为: dp[i]=(dp[1]+dp[2]+……dp[i])*i+i,简单点表述就是我在之前求得的以x(x<=i)结尾的非下降子序列后加上一个i,然后再加上i自身,这样可以保证序列不重复,至于为什么在纸上画画就知道了,直接这么做是不行的,显然会超时,区间和还有单点更新可以用线段树或者树状数组维护,以下是树状数组的实现。


#include <iostream>
#include <stdio.h>
#include <string.h>
#define ll long long
#define maxn 1000010
#define mod 1000000007
using namespace std;
ll dp[maxn],c[maxn];
int nn=1000000;
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,ll val)
{
    while(x<=nn)
    {
        c[x]=(c[x]+val)%mod;
        x+=lowbit(x);
    }
}
ll getsum(int x)
{
    ll sum=0;
    while(x>0)
    {
        sum=(c[x]+sum)%mod;
        x-=lowbit(x);
    }
    return sum;
}
int main()
{
    //freopen("dd.txt","r",stdin);
    int i,n,x;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&x);
        ll tmp=dp[x];
        dp[x]=(getsum(x)*x+x)%mod;
        add(x,((dp[x]-tmp)%mod+mod)%mod);
    }
    ll ans=0;
    for(i=1;i<=1000000;i++)
    {
        ans+=dp[i];
    }
    ans%=mod;
    cout<<ans<<endl;
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章