A题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5655
官方题解:
By NanoApe 构成四边形的条件:最长边小于其余三条边的和
坑点1:假如有条长度为0的边的话,哪里来的四条边呢?
坑点2:其余三条边的和会爆longlong,我们可以将a+b+c>d换成a>d-b-c来求解
我的理解:没想到a+b+c>d转化成a>d-b-c,想到经常自己写程序相加爆longlong的话会变成负的,那肯定是大于第四条边了,利用了下这个条件,过了
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define LL __int64 int main() { int T; LL a,b,c,d,maxn,sum,sum1; scanf("%d",&T); while(T--) { scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&d); if(a==0||b==0||c==0||d==0) { printf("No\n"); continue; } maxn=max(max(a,b),max(c,d)); sum=-maxn; sum+=a; sum1=sum; sum+=b; if(sum<sum1){printf("Yes\n");continue;} sum1=sum; sum+=c; if(sum<sum1){printf("Yes\n");continue;} sum1=sum; sum+=d; if(sum<sum1){printf("Yes\n");continue;} sum1=sum; if(sum>maxn) printf("Yes\n"); else printf("No\n"); } }
官方题解:
By YJQ 我们令dp[i][j]表示在前i个数中,选出若干个数使得它们的gcd为j的方案数,于是只需要枚举第i+1个数是否被选中来转移就可以了
令第i+1个数为v,当考虑dp[i][j]的时候,我们令dp[i+1][j]+=dp[i][j](v 不选),dp[i+1][gcd(j,v)]+=dp[i][j](v 选)
复杂度O(N*MaxV) MaxV 为出现过的数的最大值
其实有O(MaxV *log(MaxV))的做法,我们考虑记f[i]表示从这些数中选择若干个数,使得他们的gcd是i的倍数的方案数。假如有K个数是i的倍数,则f[i]=2^K-1,再用g[i]表示从这些数中选择若干个数,使得他们的gcd是i的方案数,则g[i]=f[i] - g[j] (对于所有j是i的倍数)。
由调和级数可以得到复杂度为O(MaxV *log(MaxV))
我的理解:当时做的时候,就钻进爆搜的眼里面去了,一直在想怎么预处理加快速度,就跟掉钱眼了一样,拔不出来,o(╯□╰)o,比赛的时候也一样,希望下次不要死钻一个方向,多方向解题。
dp[i]记录gcd为i的方案数
#include<cstdio> #include<cstring> using namespace std; const int N=1005; const int mod=1e8+7; #define ll __int64 int a[N],g[N][N]; ll dp[N]; int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int main() { int i,j,T,n; for(i=1;i<=1000;i++) { for(j=1;j<=1000;j++) g[i][j]=g[j][i]=gcd(i,j); } scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&a[i]); memset(dp,0,sizeof(dp)); for(i=0;i<n;i++) { for(j=1;j<=1000;j++) { if(!dp[j])continue; int t=g[a[i]][j]; dp[t]=dp[t]+dp[j]; if(dp[t]>mod) dp[t]%=mod; } dp[a[i]]++; } ll ans=0; for(i=1;i<=1000;i++) { if(!dp[i])continue; (ans+=(i*dp[i]%mod))%=mod; } printf("%I64d\n",ans); } return 0; }
官方题解:
By NanoApe 由于字符串长度最多为1000,不同的询问最多也只有500500种,那么我们先把所有询问的答案预处理出来即可。
从小到大枚举左端点,再从小到大移动右端点,用回文树维护本质不同的回文子串。
设我们现在枚举的区间是[l,r],下一步是[l,r+1],那么就相当于在回文树所维护的字符串的后面添加一个字符。当左端点改变时,重建回文树。
复杂度O(n^2)
其实还可以用manacher,在扩展的时候用Hash去重,复杂度O(n^2)
我的题解:
回文树= = 以后再看吧
#include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int maxn=1100; int n,q; char s[maxn]; int l,r; ll ans[maxn][maxn]; struct PalinTree { int ch[maxn][26],f[maxn]; int n,tot,last; int len[maxn],cnt[maxn]; int s[maxn]; int newnode(int l) { memset(ch[tot],0,sizeof(ch[tot])); cnt[tot]=0; len[tot]=l; return tot++; } void init() { tot=0; newnode(0); newnode(-1); last=0;n=0; s[n]=-1;f[0]=1; } int get_fail(int x) { while(s[n-len[x]-1]!=s[n]) x=f[x]; return x; } void add(int c) { c-='a'; s[++n]=c; last=get_fail(last); if(!ch[last][c]) { int cur=newnode(len[last]+2); f[cur]=ch[get_fail(f[last])][c]; ch[last][c]=cur; } last=ch[last][c]; cnt[last]++; } }pt; void init() { int len=strlen(s+1); for(int l=1;l<=len;l++) { pt.init(); for(int r=l;r<=len;r++) { pt.add(s[r]); ans[l][r]=pt.tot-2; } } } int main() { while(~scanf("%d",&n)) { scanf("%s",s+1); init(); scanf("%d",&q); while(q--) { scanf("%d%d",&l,&r); printf("%I64d\n",ans[l][r]); } } return 0; }