A:http://www.codeforces.com/contest/251/problem/A
求满足最大差值不大于d的3点集合的个数,枚举任意点做为最小值,然后二分找到可以作为另外两点的点有几个即可。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<math.h>
#include<string>
#include<map>
#include<set>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf = 1 << 29;
int a[100010];
int main()
{
int n,d;
scanf("%d%d",&n,&d);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
ll sum=0;
for(int i=0;i<n;i++){
int x=a[i]+d;
int left=0,right=n-1,mid,ans=-1;
while(left<=right){
mid=(left+right)>>1;
if(a[mid]<=x){
left=mid+1;
ans=mid;
}else{
right=mid-1;
}
}
if(ans>i+1){
sum+=(ll)(ans-i)*(ans-i-1)/2;
}
}
printf("%I64d\n",sum);
}
B: http://www.codeforces.com/contest/251/problem/B
很容易发现两种操作是互相抵消的,那么当一种操作连续做i次(k-i)%2能变成目标排列时,必然可以通过k次操作达到目标排列。问题要求未达到k次的时候不能变成目标排列,就要注意一种特殊情况:例如k=3,一次heads或tails都会变成目标排列,要输出NO
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<math.h>
#include<string>
#include<map>
#include<set>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf = 1 << 29;
int q[110],s[110],p[110],t[110];
int check(int n)
{
for(int i=1;i<=n;i++){
if(p[i]!=s[i]) return 0;
}
return 1;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&q[i]);
}
for(int i=1;i<=n;i++)
scanf("%d",&s[i]);
for(int i=1;i<=n;i++)
p[i]=i;
int flag=0;
if(check(n)){
printf("NO\n");
return 0;
}
int tmp=0;
for(int i=1;i<=k&&flag==0;i++){
for(int j=1;j<=n;j++){
t[j]=p[q[j]];
}
for(int j=1;j<=n;j++){
p[j]=t[j];
}
if(check(n)){
if((k-i)%2==0){
if(i==1&&k!=1)tmp=1;
else flag=1;
}
break;
}
}
for(int i=1;i<=n;i++)
p[i]=i;
for (int i = 1; i <= k&&flag==0 ; i++) {
for (int j = 1; j <= n; j++) {
t[q[j]] = p[j];
}
for (int j = 1; j <= n; j++){
p[j] = t[j];
}
if(i==1&&tmp==1&&(!check(n))){
flag=1;break;
}
if (check(n) && (k - i) % 2 == 0) {
if ((k - i) % 2 == 0){
if(i==1&&k!=1){
if(!tmp) flag=1;
}else flag=1;
}
break;
}
}
if(flag) printf("YES\n");
else printf("NO\n");
}
C: http://www.codeforces.com/contest/251/problem/C
求2到k的最小公倍数c,a->b的过程一定是a->ci->cj->b,ci,cj为c的倍数取一下余就是a%c->0 + c->0(多次)+c->b%c;c最大为360360,dp即可求解
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<math.h>
#include<string>
#include<map>
#include<set>
#include<iostream>
using namespace std;
typedef long long ll;
const int inf = 1 << 29;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
#define N 400000
int dp[N],k;
int solve(ll a,ll b)
{
fill(dp,dp+N,inf);
dp[0]=0;
for(int i=1;i<=a-b;i++){
dp[i]=min(dp[i],dp[i-1]+1);
for(int j=2;j<=k;j++){
if(i-(b+i)%j>=0)
dp[i]=min(dp[i],dp[i-(b+i)%j]+1);
}
}
return dp[a-b];
}
int main()
{
ll a,b;
int c=1;
scanf("%I64d%I64d%d",&a,&b,&k);
for(int i=2;i<=k;i++) c=c*i/gcd(c,i);
if(a/c==b/c){
printf("%d\n",solve(a,b));
}else{
printf("%I64d\n",(ll)(a/c-b/c-1)*solve(c,0)+solve(a%c,0)+solve(c,b%c));
}
}