反省:一开始用前缀积c++交了一发re,发现不能用py提交,python语法,尝试百度了java的大整数语法又交了一发TLE,其实这道题是用唯一分解定理,上上星期一场div2的第D题也是唯一分解定理,占着做3补1的心理没补,现在有点惭愧,很多知识不学就是真的不会。
思路:把各个数字分解,然后把d分解,在区间中查询对应素数出现的次数是否足够,详细见代码。
#include<bits/stdc++.h>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
#define mod 1000000007;
using namespace std;
inline ll read(){
ll s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
void put1(){ puts("Yes") ;}
void put2(){ puts("No") ;}
const int manx=1e5+5;;
vector<int>a[manx];
int main()
{
ll p=read(),x;
while(p--)
{
for(int i=1;i<manx;i++) a[i].clear(); // 初始化
ll n=read(),m=read();
for(int i=1;i<=n;i++){
x=read();
for(int j=2;j*j<=x;j++)
while(x%j==0)
a[j].push_back(i),x/=j; //存这个素数存在的下标,出现个数为可分解的次数
if(x>1) a[x].push_back(i); //如果x是素数,存入下标
}
while(m--)
{
ll l=read(),r=read(),d=read(),flag=1;
for(int i=2;i*i<=d;i++)
{
int ans=0; //ans统计素数p的幂
while(d%i==0) ans++,d/=i;
if(ans){ //t为查询到l到r的区间中i出现次数
int t=upper_bound(a[i].begin(),a[i].end(),r)-lower_bound(a[i].begin(),a[i].end(),l);
if(t<ans){ //出现次数一定要比ans大,不然无法整除d
flag=0;
break;
}
}
}
if(d>1) { //如果d为素数,那么只需要出现一次即可
int t=upper_bound(a[d].begin(),a[d].end(),r)-lower_bound(a[d].begin(),a[d].end(),l);
if(t<1) flag=0;
}
if(flag) put1();
else put2();
}
}
return 0;
}
思路:字符串前缀和,从小枚举字母,判断左右区间出现次数是否相同,相同时即该字母没有出现过,详细见代码。
#include<bits/stdc++.h>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
#define mod 1000000007;
using namespace std;
inline ll read(){
ll s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
void put1(){ puts("Yes") ;}
void put2(){ puts("No") ;}
const int manx=1e5+5;;
int a[manx][30];
char s[manx];
int main()
{
ll p=read(),x=1;
while(p--)
{
memset(a,0,sizeof(a));
ll n=read(),m=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
a[i][s[i]-'A']++; //字母出现次数 一维是出现的下标,二维是字母
for(int j=0;j<26;j++)
a[i+1][j]=a[i][j]; //累加前面出现的次数
}
printf("Case #%lld:\n",x++);
while(m--)
{
ll l=read(),r=read();
for(int i=0;i<26;i++)
if(a[l-1][i]!=a[r][i])
//如果左区间前一个区间跟右区间出现次数相同,即该字母没有出现过
{
cout<<a[r][i]-a[l-1][i]<<endl;
break;
}
}
}
return 0;
}
反省:看到跟树有关就做题兴致不高,缺乏竞技精神,就算是树也是要试一下。
思路:最小路径和肯定要设置权值小的边离根比较近,所以先排序,然后用b数组维护每层的成本(边权和),每一层可由上一层的边权和*n,再按边权大小累加本层的成本,最后每层叠加求和取模即可。
#include<bits/stdc++.h>
#define ll long long
#define R register ll
#define inf 0x3f3f3f3f
#define mod 1000000007;
using namespace std;
inline ll read(){
ll s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
void put1(){ puts("Yes") ;}
void put2(){ puts("No") ;}
const ll manx=2e5+5;;
ll k,m,n,p;
ll a[manx],b[manx]; //a是边的集合 b是第i个间隔层的成本
int main()
{
while(scanf("%lld%lld%lld%lld",&k,&m,&n,&p)!=EOF){ //hdu多组输入
// k=read(),m=read(),n=read(),p=read();
memset(a,0,sizeof(a)); //初始化
memset(b,0,sizeof(b));
for(ll i=1;i<=k;i++){
a[i]=read(); //这里不能取模,取模的话会影响后面的计算,wa点1
}
sort(a+1,a+1+k);
ll index=1,res=n;
m--; //这里减一是为了算层数之间的间隔,wa点2
for(ll i=1;i<=m;i++){ //遍历每个间隔
b[i]=(b[i-1]*n)%p; //上一层的成本* 开叉数
for(ll j=1;j<=res;j++) // 每层的分支数
b[i]=(b[i]+a[index++])%p; //叠加
res*=n; //每层叉出n倍的节点
}
ll ans=0;
for(ll i=1;i<=m;i++)
ans=(ans+b[i])%p;
cout<<ans<<endl;
}
return 0;
}