Solved | Pro.ID | Title | Author | Source | (AC/Submit)Ratio |
5380 | Travel with candy | SXYZ | 2015 Multi-University Training Contest 8 | (46/103)44.66% | |
5381 | The sum of gcd | SXYZ | 2015 Multi-University Training Contest 8 | (208/496)41.94% | |
5382 | GCD?LCM! | SXYZ | 2015 Multi-University Training Contest 8 | (73/139)52.52% | |
5383 | Yu-Gi-Oh! | SXYZ | 2015 Multi-University Training Contest 8 | (95/366)25.96% | |
5384 | Danganronpa | SXYZ | 2015 Multi-University Training Contest 8 | (278/506)54.94% | |
5385 | The path | SXYZ | 2015 Multi-University Training Contest 8 | (202/546)37.00% | |
5386 | Cover | SXYZ | 2015 Multi-University Training Contest 8 | (377/1092)34.52% | |
5387 | Clock | SXYZ | 2015 Multi-University Training Contest 8 | (325/469)69.30% | |
5388 | Geometer's Sketchpad | SXYZ | 2015 Multi-University Training Contest 8 | (21/58)36.21% | |
5389 | Zero Escape | SXYZ | 2015 Multi-University Training Contest 8 | (485/953)50.89% | |
5390 | tree | SXYZ | 2015 Multi-University Training Contest 8 | (28/160)17.50% |
HDU 5381
给定一个数组,多次询问,求区间[l,r]内所有子区间的最大公约数之和。
以a[x]为右端点的gcd区间情况最多只有log(a[x])种,因为a[x]的因子只有sqrt(a[x])种,a[x]的因子的因子只有sqrt(sqrt(a[x]))种,....依次类推共有log(a[x])种
如下图x=7的情况
用sum[x]表示以x为右端点的所有gcd区间和
sum[x]=gcd[1]*(r1-l1+1)+gcd[2]*(r2-l2+1)+...+gcd[k]*(rk-lk+1) (k<=log(a[x]))
遍历数组,用树状数组维护sum,区间更新,动态查询。
用f[l,r]表示区间[l,r]的所有子区间的gcd和,用d[i]表示区间[1,i]同时增加或者减少的量
f[l,r]=d[l]*1+d[l+1]*2+...+d[r]*(r-l+1)
=sigma(d[i]*(i-l+1))
=sigma(d[i]*i) - (l-1)sigma(d[i]) (l<=i<=r)
用两个数组分别维护d[i] 和 d[i]*i 即可。
计算gcd区间的位置时用了一个nxt数组,表示以x为右端点时区间[nxt[j],j]的gcd值相同
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define low(x) (x&(-x)) #define maxn 10010 #define LL long long int n,m; int a[maxn]; LL ans[maxn]; int nxt[maxn]; int val[maxn]; struct node { int l,r,id; bool operator < (const node &b)const { return r<b.r; } }b[maxn]; LL bit[maxn*2][2]; void add(int t,int x,LL val) { while(x>0) { bit[x][t]+=val; x-=low(x); } } LL getsum(int t,int x) { LL ans=0; while(x<maxn) { ans+=bit[x][t]; x+=low(x); } return ans; } void update(int l,int r,int val) { add(0,r,(LL)val); add(0,l-1,-(LL)val); add(1,r,(LL)val*r); add(1,l-1,-(LL)val*(l-1)); } int gcd(int a,int b) { if(a%b==0)return b; else return gcd(b,a%b); } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&b[i].l,&b[i].r); b[i].id=i; } sort(b+1,b+m+1); memset(bit,0,sizeof(bit)); int k=1; nxt[1]=1; val[1]=a[1]; update(1,1,a[1]); for(int i=2;i<=n;i++) { val[i]=a[i]; nxt[i]=i; int j=i; while(j>0){ val[j]=gcd(val[j],a[i]); while(nxt[j]>1&&gcd(val[nxt[j]-1],val[j])==val[j]) { nxt[j]=nxt[nxt[j]-1]; } update(nxt[j],j,val[j]); // printf("i=%d %d %d %d\n",i,nxt[j],j,val[j]); j=nxt[j]-1; } while(k<=m&&b[k].r==i) { ans[b[k].id]=getsum(1,b[k].l)-(b[k].l-1)*getsum(0,b[k].l); k++; } } // printf("k=%d\n",k); for(int i=1;i<=m;i++) { // printf("l=%d r=%d ans=%d\n",b[i].l,b[i].r,ans[b[i].id]); printf("%lld\n",ans[i]); } } return 0; } /* 111 7 1 6 2 3 6 30 12 0 2 5 1 2 3 4 5 15 1 1 1 2 1 3 1 4 1 5 2 2 2 3 2 4 2 5 3 3 3 4 3 5 4 4 4 5 5 5 */
HDU5383
有n个人,每个人有一个数字s[i], 有两扇门的数字a, b 。数字都可以重复。现要求将这n个人分配到这两个房间,或者只分配到一个房间,问方法数
x的数字根=(x-1)%9+1
dp[i][j]表示前i个人中k个人分配到房间号为j的方法数 (0<=k<=i)
dp[0][0]=0;
最后要分类讨论,先计算所有人的和的数组根sum
sum==root(a+b) 表示两个房间都有人的情况ans=dp[n][a],如果同时sum==a ,则dp[n][a]中包含了只有a的那种情况 ans=dp[n][a]-1
sum==a 表示只有a房间有人的情况
sum==b表示只有b房间有人的情况
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; #define LL long long #define maxn 100010 #define mod 258280327 int s[maxn],n,a,b; int dp[maxn][10]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&a,&b); for(int i=1;i<=n;i++)scanf("%d",&s[i]); memset(dp,0,sizeof(dp)); dp[0][0]=1; int sum=0; for(int i=1;i<=n;i++) { sum=(sum+s[i]-1)%9+1; for(int j=0;j<=9;j++) dp[i][j]=dp[i-1][j]; for(int j=0;j<=9;j++) { int t=(j+s[i]-1)%9+1; dp[i][t]=(dp[i][t]+dp[i-1][j])%mod; } } if(sum!=a && sum!=b && sum!=(a+b-1)%9+1) { puts("0"); continue; } int ans=0; if(sum==(a+b-1)%9+1) { ans+=dp[n][a]; if(sum==a)ans--; } if(sum==a)ans++; if(sum==b)ans++; printf("%d\n",ans); } return 0; }
HDU5384
先给n条询问,再给m个单词(模式串),求模式串出现的次数和
ac自动机模板题
#include<string.h> #include<algorithm> #include<iostream> #include<stdio.h> #include<queue> using namespace std; char str[100010][10010]; int num[100010],n,m; struct Trie { int next[10010*50][28],fail[10010*50],end[10010*50]; int root,L; int newnode() { for(int i=0; i<26; i++) { next[L][i]=-1; } end[L++]=-1; return L-1; } void init() { L=0; root=newnode(); } void insert(char *s) { int len=strlen(s); int now=root; for(int i=0; i<len; i++) { if(next[now][s[i]-'a']==-1) { next[now][s[i]-'a']=newnode(); } now=next[now][s[i]-'a']; } if(end[now]==-1)///标记模式串出现的次数 { end[now]=1; } else { end[now]++; } } void build() { queue<int>Q; fail[root]=root; for(int i=0; i<26; i++) { if(next[root][i]==-1) { next[root][i]=root; } else { fail[next[root][i]]=root; Q.push(next[root][i]); } } while(!Q.empty()) { // printf("**\n"); int now=Q.front(); Q.pop(); for(int i=0; i<26; i++) { if(next[now][i]==-1) next[now][i]=next[fail[now]][i]; else { fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } } void query(char* s) { memset(num,0,sizeof(num)); int len=strlen(s); int now=root; for(int i=0; i<len; i++) { now=next[now][s[i]-'a']; int temp=now; while(temp!=root) { if(end[temp]!=-1)///统计所有模式串出现的次数,num数组在0~m之间定能取到所有end[temp]必不大于m { num[end[temp]]+=end[temp]; } temp=fail[temp]; } } int ans=0; for(int i=0; i<=m; i++) { if(num[i]>0) ans+=num[i]; } printf("%d\n",ans); } } ac; char s[10005]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0; i<n; i++) { scanf("%s",str[i]); } ac.init(); for(int i=0; i<m; i++) { scanf("%s",s); ac.insert(s); } ac.build(); //printf("**\n"); for(int i=0; i<n; i++) { ac.query(str[i]); } } return 0; }
HDU5386
给你一个n*n原始矩阵和结果矩阵,有以下两种操作,给你m个操作,要求将操作排序,使得原始矩阵转为结果矩阵
L x y: for(int i=1;i<=n;i++)color[i][x]=y;
H x y:for(int i=1;i<=n;i++)color[x][i]=y;
结果矩阵肯定有一行或者一列数字是相同的,只用从结果矩阵出发,每次将数字相同的一行或者一列查询对应的操作,然后标记为-1(-1表示任何颜色都可以),原始矩阵并没有什么卵用。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m; int s[110][110]; struct node { char op[2]; int x,y; }a[550]; int v[550]; int ans[550]; int ok(int k) { int x=a[k].x; int y=a[k].y; if(a[k].op[0]=='L') { for(int i=1;i<=n;i++) { if(s[i][x]!=-1&&s[i][x]!=y)return 0; } for(int i=1;i<=n;i++)s[i][x]=-1; } else{ for(int i=1;i<=n;i++) { if(s[x][i]!=-1&&s[x][i]!=y)return 0; } for(int i=1;i<=n;i++)s[x][i]=-1; } return 1; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { scanf("%d",&s[i][j]); } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { scanf("%d",&s[i][j]); } } for(int i=1;i<=m;i++) { scanf("%s%d%d",a[i].op,&a[i].x,&a[i].y); } memset(v,0,sizeof(v)); int k=1; for(int i=1;i<=m;i++) { for(int j=1;j<=m;j++) { if(!v[j]&&ok(j)) { v[j]=1; ans[k++]=j; break; } } } for(int i=m;i>1;i--)printf("%d ",ans[i]); printf("%d\n",ans[1]); } return 0; }
HDU5387
给你一个时间 计算时针和分针的夹角,时针和秒针的夹角,分针和秒针的夹角
全部转为秒再通分即可
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; const int fen=12*3600; int hh,mm,ss; int a[4],b[4]; int run(int a,int b) { int ans=abs(a-b); ans%=fen; if(ans>fen/2)ans=fen-ans; return ans; } int gcd(int a,int b) { if(a%b==0)return b; return gcd(b,a%b); } void print(int x) { x*=360; int g=gcd(x,fen); int t1=x/g; int t2=fen/g; if(t2==1)printf("%d",t1); else printf("%d/%d",t1,t2); } int main() { int T; scanf("%d",&T); char str[100]; while(T--) { scanf("%s",str); sscanf(str,"%d:%d:%d",&hh,&mm,&ss); if(hh>=12)hh-=12; a[1]=hh*3600+60*mm+ss; a[2]=12*(60*mm+ss); a[3]=12*60*ss; b[1]=run(a[1],a[2]); b[2]=run(a[1],a[3]); b[3]=run(a[2],a[3]); for(int i=1;i<=3;i++) { print(b[i]); printf(" "); } puts(""); } return 0; }