codeforces round #174 div2

http://codeforces.com/contest/284/problem/A

A:题意:求一个质数的原根

 

思路:直接暴力即可,不过貌似有公式,求p-1的欧拉函数即可,至于证明,本弱菜不会。

 

http://codeforces.com/contest/284/problem/B

B:题意:题意不太好明白,N个人打牌,每人有一个状态,有A I F三种状态,一个人可以将手展示在桌上(不知道是不是这么翻译,感觉有点怪怪的)当且仅当1:这个人的状态不是F。2:其他人不能有I状态。

 

思路:当有两个以上I时,答案为零,当有1个I时,答案为1,当没有I时,答案为A的个数。

 

http://codeforces.com/contest/284/problem/C

C:题意:给一个数列,初始只有一个元素0,三种操作:

1:将前i个元素加上x。

2:在数列最后加上一个元素x。

3:删掉数列最后一个元素。

对于每一个操作,求操作后数列中各数的平均值。

 

思路:我用线段树做的(应该还有更好的方法,暂时没研究,以后再补吧),每个操作都很好处理,维护数列各数的和即可,再维护一个变量保存数列的大小即可。

 

http://codeforces.com/contest/284/problem/D

 

题意:题意应该都明白吧,有点太长,这里不写了。

 

思路:首先容易观察到,当跳到a[i]时,x一定为i,这时有两个情况,下一步是跳到i+a[i]或者是i-a[i],对于每一种情况路线是固定的,所以我们可以设dp[i][0]为初始在第i个数,下一步跳到第i-a[i]个数时y最终的值,dp[i][1]为初始在第i个数,下一步跳到第i+a[i]个数时y的最终的值(若出现无限循环则为-1)。一共就2*n个状态,我们可以用记忆化搜索实现,对于初始状态,对于dp[i][0],因为当跳到第1个数时,x=1,因为a[1]>=1,所以x-a[1]<=0,所以dp[1][0]=0;

对于dp[i][1],因为我们初始时就是从1开始,而且下一步是跳到第1+a[1]个数的,若再次到达dp[1][1],则说明一定出现了循环,所以dp[1][1]=-1。以下是代码。

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 200010
using namespace std;
long long dp[maxn][2];
int a[maxn],vis[maxn][2],op[2]={-1,1};
int n;
long long dfs(int x,int t)
{
    if(x<=0||x>n)
    return 0;
    if(vis[x][t])
    {
        return dp[x][t];
    }
    vis[x][t]=1;
   long long temp=dfs(x+op[t]*a[x],1-t);
    if(temp==-1)
    return dp[x][t]=-1;
    return dp[x][t]=temp+a[x];
}
int main()
{
   freopen("dd.txt","r",stdin);
    memset(dp,-1,sizeof(dp));
    memset(vis,0,sizeof(vis));
    scanf("%d",&n);
    int i;
    for(i=2;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    vis[1][1]=vis[1][0]=1;
    dp[1][0]=0;
    for(i=2;i<=n;i++)
    {
        if(!vis[i][0])
        dfs(i,0);
    }
    for(i=2;i<=n;i++)
    {
        if(dp[i][0]!=-1)
        printf("%I64d\n",dp[i][0]+i-1);
        else
        printf("-1\n");
    }
    return 0;
}


 

http://codeforces.com/contest/284/problem/E

E:思路:这道题有两个关键点:

1:首先我们将每种硬币看成点,将两个硬币之间的大小关系看成一条有向边,(若bi多于ci,则从bi连一条边向ci),则成了一张有向图,如果这个有向图存在环,则显然无解,输出0,否则,因为bi互不相等,ci互不相等,所以这个图变成了一条一条的链,每条链相互独立。

2:对于一条链 c1->c2->......->ck,ck的个数最少为0,ck-1的个数最少为1,......c1最少为k-1,则我们事先将(k-1)*a[1]+(k-2)*a[2]+......+ck-1*1从T中减去,变成T',剪完后,这时如果我们要再添加一种硬币cj(1<=j<=k),则我们必须再添加cj-1,cj-2,.....c1才能满足它们之间的大小关系,所以这时我们可以把aj看成价值为(a1+a2+.....aj)的硬币,所以我们可以把这k种硬币重新分配价值,每种硬币的价值为wi(按前面那种方式分配),使得它们之间相互独立,故可将原题转化成有n种硬币,每种硬币价值为wi,求价值和达到T'的方案数。这即是经典的完全背包问题了,用O(T'n)的复杂度即可解决。记住当T'<0时答案为0。代码如下:

 

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 100010
#define mod 1000000007
using namespace std;
long long dp[maxn];
int a[310],in[310];
long long tmp[310];
struct edge
{
    int to;
    int next;
}e[310];
int box[maxn],cnt;
long long T;
void init()
{
    memset(box,-1,sizeof(box));
    memset(in,0,sizeof(in));
    memset(dp,0,sizeof(dp));
    cnt=0;
}
void add(int from,int to)
{
    e[cnt].to=to;
    e[cnt].next=box[from];
    box[from]=cnt++;
}
long long dfs(int now)
{
    tmp[now]=a[now];
    int t,v;
    for(t=box[now];t+1;t=e[t].next)
    {
        v=e[t].to;
        long long tt=dfs(v);
        tmp[now]+=tt;
    }
    T-=tmp[now];
    return tmp[now];
}
bool iscircle(int n)
{
    int qu[310],tmp[310];
    int i,sum=0,top=0;
    for(i=1;i<=n;i++)
    {
        tmp[i]=in[i];
        if(tmp[i]==0)
        qu[top++]=i;
    }
    for(i=0;i<top;i++)
    {
        int now=qu[i],t,v;
        for(t=box[now];t+1;t=e[t].next)
        {
            v=e[t].to;
            tmp[v]--;
            if(!tmp[v])
            qu[top++]=v;
        }
    }
    if(top<n)
    return true;
    return false;
}
int main()
{
    //freopen("dd.txt","r",stdin);
    int n,q,i,aa,b,j;
    scanf("%d%d%I64d",&n,&q,&T);
    for(i=1;i<=n;i++)
    scanf("%d",&a[i]);
    init();
    for(i=1;i<=q;i++)
    {
        scanf("%d%d",&aa,&b);
        in[aa]++;
        add(b,aa);
    }
    bool tru=iscircle(n);
    for(i=1;i<=n;i++)
    {
        if(!in[i])
        {
            dfs(i);
            T+=tmp[i];
        }
    }
    if(tru||T<0)
    printf("0\n");
    else
    {
        dp[0]=1;
        for(i=1;i<=n;i++)
        {
            for(j=tmp[i];j<=T;j++)
            dp[j]=(dp[j]+dp[j-tmp[i]])%mod;
        }
        printf("%I64d\n",dp[T]);
    }
    return 0;
}


 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值