HDU 4407 SUM 容斥原理
这个题目要求既对序列的某段区间求和p互质数目,又支持对某个数进行修改
一开始看到这个题,有点懵,就是因为第二个操作,如果只是第一个操作,并且是原始序列1-n,那就很简单了,按照之前写过的某个题目,求出1-y中的数目,减去1-x中的数目即可
就是第二个操作把序列打乱了,这样就不好搞了
后来还是大神博客的一句话,突然解决了问题,说:总的操作数只有1000,直接暴力搞定第二个操作就行啊
我想想,对啊,一开始只是按普通操作求出数目,然后遍历一下修改(都最多只有1000个),把修改对应投射到结果上,就行了啊。。。于是,迎仍而解。。。
怎么没想到可以暴力呢,太把第二个操作当回事了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define LL __int64 using namespace std; const int N = 400010; int change[N],n,m; int calc[1010],cnt; int prime[20],ret; int gcd(int a,int b) { if (a<b) swap(a,b); while (b) { int tmp=b; b=a%b; a=tmp; } return a; } void proc(int x) { ret=0; for (int i=2;i*i<=x;i++){ if (x%i==0){ prime[ret++]=i; while (x%i==0) x/=i; } } if (x>1) prime[ret++]=x; } LL dfs(int d,int co,int sign,int x) { if (d>=ret) return 0; LL sum=0; if (d>=0){ LL res=x/co; if (res>0) sum=(LL)res*co+res*(res-1)/2*co; sum*=(LL)sign; } for (int i=d+1;i<ret;i++){ sum+=dfs(i,co*prime[i],-sign,x); } return sum; } int main() { int t,x,y,p,op; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); //memset(change,0,sizeof(change[0]*(n+2))); cnt=0; while (m--) { scanf("%d",&op); if (op==2){ scanf("%d%d",&x,&y); change[x]=y; bool flag=0; for (int i=0;i<cnt;i++){ if (calc[i]==x) {flag=1;break;} } if (!flag) calc[cnt++]=x; } else{ scanf("%d%d%d",&x,&y,&p); if (x>y) swap(x,y); proc(p); LL sum1=dfs(-1,1,-1,x-1); LL sum2=dfs(-1,1,-1,y); //cout<<sum1<<" "<<sum2<<endl; sum2-=sum1; LL all=(LL)(x+y)*(y-x+1)/2; all-=sum2; for (int i=0;i<cnt;i++){ if (calc[i]>=x && calc[i]<=y){ if (gcd(calc[i],p)==1) all-=(LL)calc[i]; if (gcd(change[calc[i]],p)==1) all+=(LL)change[calc[i]]; } } printf("%I64d\n",all); } } } }