hdu 6312——Game
题意:给一个数字n,就代表有一个1~n的序列,A和B两个人分别可以对这个序列进行操作,每次操作可以删去一个数及这个数所有的因子,轮到谁时谁无法再进行操作谁就输了,也就是刚好删除最后一个数的人赢。A先走,问A能不能赢。
一看就能知道这是一道博弈论的题,先分析一下这个规则,从1-n,我们可以先不看1,因为删除任何一个数都可以顺便把1删除了,那么只从2~n中选择就一定有一个胜者,如果这个状态A胜的话,我们就可以选择那个让我们胜的数,如果A输,我们可以先去掉1来转变状态,这样A还是会胜。所以A是必胜的,只用输出YES就行了。
附上代码(注意多组输入):
#include
using namespace std;
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
printf("Yes\n");
}
return 0;
}
hdu 6318——Swaps and Inversions
题意:给一串数字序列,如果一个数 a[i] 的逆序数 n[i] 不为0,那么要付出 n[i]*x 的代价,但是也可以将它与相邻位置的数交换减少它的逆序数,交换一次的代价是y,问至少要付出的代价是多少。
首先要知道什么是逆序数,逆序数的概念是,给定一个序列{An},对于An中的任何一个元素Ai(1≤i≤n),其前面i-1个元素比Ai大的元素个数j的总和Σj为该序列的逆序数。其次我们要明白,把一个逆序数不为0的序列中的数要交换到所有正确的位置使这个序列的逆序数为0的,交换的次数就等于这个序列的逆序数。这么说有点抽象,我们可以模拟一下:
有一个序列: 4 2 3 1 2
逆序数为 : 0 1 1 3 2
诶。。。好像在电脑上模拟这个交换很麻烦,麻烦自己手推一下。
所以我们只用判断x和y谁小,就用谁。然后关键就是求出逆序数,然后用逆序数乘上x和y之中比较小的那个数,求逆序数可以有很多方法,这里附上树状数组+离散化的代码:
#include
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
int a[maxn],b[maxn],c[maxn];
int n;
LL x,y;
inline void add(int pos)
{
while(pos<=n){
c[pos]++;
pos+=pos&(-pos);
}
}
inline int getsum(int pos)
{
int ret=0;
while(pos>0){
ret+=c[pos];
pos-=pos&(-pos);
}
return ret;
}
int main()
{
while(scanf("%d%lld%lld",&n,&x,&y)!=EOF){
for(int i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int tot=1;
for(int i=2;i<=n;i++){
if(b[i]!=b[i-1]) b[++tot]=b[i];
}
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
LL sum=0;
for(int i=n;i>=1;i--){
sum+=1LL*getsum(a[i]-1);
add(a[i]);
}
if(x>=y)
sum=sum*y;
else sum=sum*x;
printf("%lld\n",sum);
for(int i=0;i<=n;i++)
c[i]=0;
}
return 0;
}
hdu 6315——Naive Operations
题意:有两个数字序列An和Bn,An里的元素一开始全为0,Bn数组由题目给出,现在有两种操作
add l r 表示在An数组区间( l , r )中的每一个数都加1,
query l r 表示查询区间( l , r )的ai/bi的和。
因为涉及到区间更新区间和区间求和,所以考虑线段树,当然如果单纯的用线段树一点一点更新肯定复杂度太高,所以在这里我们可以利用这道题的特性,只有当 ai 加1的次数能够整除 bi 时才会对区间答案有贡献,这样用线段树记录一开始每个点初始化为bi,每次访问的区间有这个点就减1,当为0时表示可以整除bi此位置的贡献加1,然后向下查找初始的bi的值重新赋值,而当查询区间的和时,加上每个点的贡献就行了。大体思路就这样,贴一下标程题解。
附上代码:
#include
using namespace std;
const int maxn=100010;
struct node
{
int l,r;
int num;//记录当前位置的状态,一开始为b的值,为0时表示当前可以被整除
int sum;//记录当前点整除的结果
int lazy;//懒惰标记
int b;//记录b数组的初始值
} a[maxn*4];
void buildtree(int l,int r,int rt)//建树
{
a[rt].l=l;
a[rt].r=r;
a[rt].sum=0;
a[rt].lazy=0;
if(l==r)//找到叶子节点
{
scanf("%d",&a[rt].b);
a[rt].sum=0;
a[rt].num=a[rt].b;
return;
}
int mid=(l+r)/2;
buildtree(l,mid,2*rt);
buildtree(mid+1,r,2*rt+1);
a[rt].num=min(a[2*rt].num,a[2*rt+1].num);//维护区间最小值
}
void pushdown(int rt)//向下更新延迟标记
{
if(a[rt].lazy)
{
a[2*rt].lazy+=a[rt].lazy;
a[2*rt+1].lazy+=a[rt].lazy;
a[2*rt].num+=a[rt].lazy;
a[2*rt+1].num+=a[rt].lazy;
a[rt].lazy=0;
}
}
void update(int l,int r,int rt)//向上更新
{
if(a[rt].l==l&&a[rt].r==r)
{
a[rt].num--;
if(a[rt].num)///当前区间没有可以整除的点
{
a[rt].lazy--;
return;
}
else ///有可以整除的点
{
if(a[rt].l==a[rt].r)
{
a[rt].sum++;
a[rt].num=a[rt].b;
return;
}
}
}
pushdown(rt);
int mid=(a[rt].l+a[rt].r)/2;
if(r<=mid)
update(l,r,2*rt);
else if(l>mid)
update(l,r,2*rt+1);
else
{
update(l,mid,2*rt);
update(mid+1,r,2*rt+1);
}
a[rt].num=min(a[2*rt].num,a[2*rt+1].num);//维护区间最小值
a[rt].sum=a[2*rt].sum+a[2*rt+1].sum;
}
int query(int l,int r,int rt)
{
if(a[rt].r==r&&a[rt].l==l)
{
return a[rt].sum;
}
int mid=(a[rt].l+a[rt].r)/2;
if(r<=mid)
return query(l,r,2*rt);
else if(l>mid)
return query(l,r,2*rt+1);
else
{
return query(l,mid,2*rt)+query(mid+1,r,2*rt+1);
}
}
int main()
{
int n,m;
int l,r;
char s[10];
while(scanf("%d%d",&n,&m)!=EOF)
{
buildtree(1,n,1);
for(int i=1; i<=m; i++)
{
scanf("%s%d%d",s,&l,&r);
if(s[0]=='a')
{
update(l,r,1);
}
else
{
int ans=query(l,r,1);
printf("%d\n",ans);
}
}
}
return 0;
}