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;
}