problem 1001(hdu 4632)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4632
Palindrome subsequence
思路:记忆化DP,
dp[i][j]表示原字符串中[i,j]位置中出现的回文子序列的个数
递推关系:
dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]
如果str[i]==str[j]
dp[i][j]+=dp[i+1][j-1]
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1010; 6 const int mod=10007; 7 int dp[maxn][maxn]; 8 char str[maxn]; 9 int dfs(int l,int r) 10 { 11 if(dp[l][r]!=-1) 12 return dp[l][r]; 13 if(l==r) 14 { 15 dp[l][r]=1; 16 return dp[l][r]; 17 } 18 if(l+1==r) 19 { 20 if(str[l]==str[r]) 21 { 22 dp[l][r]=3; 23 } 24 else 25 dp[l][r]=2; 26 return dp[l][r]; 27 } 28 int t=dfs(l+1,r-1); 29 dp[l][r]=0; 30 dp[l][r]+=dfs(l+1,r)+dfs(l,r-1)-t; 31 if(str[l]==str[r]) 32 dp[l][r]+=t+1; 33 dp[l][r]%=mod; 34 if(dp[l][r]<0)//一开始没有考虑到 35 dp[l][r]+=mod; 36 return dp[l][r]; 37 } 38 int main() 39 { 40 int T; 41 scanf("%d",&T); 42 int k=0; 43 while(T--) 44 { 45 getchar(); 46 scanf("%s",str); 47 k++; 48 int len=strlen(str); 49 memset(dp,-1,sizeof(dp)); 50 printf("Case %d: %d ",k,dfs(0,len-1)); 51 } 52 return 0; 53 }
problem 1004(hdu 4635)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4635
Strongly connected
思路:利用tarjan算法,将图分成多个强连通分量子图,查找出出度或入度为零并且子图点数最少的连通分量子图(假设称为最小子图),其点数为mmin
将除最小子图的剩余部分连成完全有向图,把最小子图的点之间连成完全有向图,最后只用单向边连接两部分,减去原来的边数m即可;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<stack> 7 using namespace std; 8 const int maxn=100010; 9 struct node 10 { 11 int u; 12 int v; 13 int next; 14 }edge[maxn]; 15 int head[maxn]; 16 int in[maxn],out[maxn]; 17 int dfn[maxn]; 18 int low[maxn]; 19 int sccno[maxn]; 20 int num[maxn]; 21 int cn,deep,sccnum; 22 stack<int>st; 23 void init() 24 { 25 memset(head,-1,sizeof(head)); 26 memset(dfn,0,sizeof(dfn)); 27 memset(sccno,0,sizeof(sccno)); 28 memset(in,0,sizeof(in)); 29 memset(out,0,sizeof(out)); 30 memset(num,0,sizeof(num)); 31 deep=0; 32 sccnum=0; 33 cn=0; 34 while(!st.empty()) 35 { 36 st.pop(); 37 } 38 } 39 void add(int u,int v) 40 { 41 edge[cn].u=u; 42 edge[cn].v=v; 43 edge[cn].next=head[u]; 44 head[u]=cn++; 45 } 46 void tarjan(int u) 47 { 48 st.push(u); 49 low[u]=dfn[u]=++deep; 50 int i; 51 for(i=head[u];i!=-1;i=edge[i].next) 52 { 53 int v=edge[i].v; 54 if(!dfn[v]) 55 { 56 tarjan(v); 57 low[u]=min(low[u],low[v]); 58 } 59 else if(!sccno[v]) 60 { 61 low[u]=min(low[u],dfn[v]); 62 } 63 } 64 if(low[u]==dfn[u]) 65 { 66 sccnum++; 67 while(1) 68 { 69 int x=st.top(); 70 st.pop(); 71 sccno[x]=sccnum; 72 if(x==u) 73 break; 74 } 75 } 76 } 77 int main() 78 { 79 int T; 80 scanf("%d",&T); 81 int k=0; 82 while(T--) 83 { 84 __int64 m,n; 85 scanf("%I64d%I64d",&n,&m); 86 k++; 87 88 int i,j; 89 90 init(); 91 for(i=0;i<m;i++) 92 { 93 int u,v; 94 scanf("%d%d",&u,&v); 95 add(u,v); 96 } 97 for(i=1;i<=n;i++) 98 { 99 if(!dfn[i]) 100 { 101 tarjan(i); 102 } 103 } 104 //printf("scc=%d",sccnum); 105 if(sccnum==1) 106 { 107 printf("Case %d: -1 ",k); 108 continue; 109 } 110 for(i=1;i<=n;i++) 111 { 112 num[sccno[i]]++; 113 for(j=head[i];j!=-1;j=edge[j].next) 114 { 115 int v=edge[j].v; 116 if(sccno[i]!=sccno[v]) 117 { 118 out[sccno[i]]++; 119 in[sccno[v]]++; 120 } 121 } 122 } 123 __int64 mmin=1e13; 124 for(i=1;i<=sccnum;i++) 125 { 126 if(in[i]==0||out[i]==0) 127 { 128 if(mmin>num[i]) 129 mmin=num[i]; 130 } 131 } 132 __int64 res=0; 133 res+=(n-mmin)*(n-mmin-1); 134 res+=mmin*(mmin-1); 135 res+=(n-mmin)*mmin; 136 res-=m; 137 printf("Case %d: %I64d ",k,res); 138 } 139 }
problem 1007(hdu 4638)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4638
思路:(官方题解)
题意为询问一段区间里的数能组成多少段连续的数。先考虑从左往右一个数一个数添加,考虑当前添加了i - 1个数的答案是x,那么添加完i个数后的答案是多少?可以看出,是根据a[i]-1和a[i]+1是否已经添加而定的,如果a[i]-1或者a[i]+1已经添加一个,则段数不变,如果都没添加则段数加1,如果都添加了则段数减1。设v[i]为加入第i个数后的改变量,那么加到第x数时的段数就是sum{v[i]} (1<=i<=x}。仔细想想,若删除某个数,那么这个数两端的数的改变量也会跟着改变,这样一段区间的数构成的段数就还是他们的v值的和。将询问离线处理,按左端点排序后扫描一遍,左边删除,右边插入,查询就是求区间和。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=100010; 7 int data[maxn]; 8 int pos[maxn]; 9 int in[maxn]; 10 int tree[maxn]; 11 int ss[maxn]; 12 int res[maxn]; 13 int n,m; 14 struct node 15 { 16 int l; 17 int r; 18 int xh; 19 } que[maxn]; 20 bool cmp(struct node a,struct node b) 21 { 22 if(a.l==b.l) 23 return a.r<b.r; 24 else 25 return a.l<b.l; 26 } 27 int lowbit(int x) 28 { 29 return x&(-x); 30 } 31 void update(int s,int num) 32 { 33 while(s<=n) 34 { 35 tree[s]+=num; 36 s+=lowbit(s); 37 } 38 } 39 int query(int s) 40 { 41 int sum=0; 42 while(s>0) 43 { 44 sum+=tree[s]; 45 s-=lowbit(s); 46 } 47 return sum; 48 } 49 int main() 50 { 51 int T; 52 scanf("%d",&T); 53 while(T--) 54 { 55 memset(tree,0,sizeof(tree)); 56 memset(pos,0,sizeof(pos)); 57 scanf("%d%d",&n,&m); 58 int i; 59 for(i=1; i<=n; i++) 60 { 61 scanf("%d",&data[i]); 62 pos[data[i]]=i; 63 } 64 int l,r; 65 for(i=0; i<m; i++) 66 { 67 scanf("%d%d",&l,&r); 68 que[i].l=l; 69 que[i].r=r; 70 que[i].xh=i; 71 } 72 sort(que,que+m,cmp); 73 memset(in,0,sizeof(in)); 74 memset(ss,0,sizeof(ss)); 75 for(i=1; i<=n; i++) 76 { 77 if(in[data[i]-1])ss[i]++; 78 if(in[data[i]+1])ss[i]++; 79 if(ss[i]==0) 80 update(i,1); 81 if(ss[i]==2) 82 update(i,-1); 83 in[data[i]]=1; 84 } 85 l=1; 86 for(i=0; i<m; i++) 87 { 88 for(; l<que[i].l; l++) 89 { 90 if(data[l]>1&&pos[data[l]-1]>l) 91 { 92 update(pos[data[l]-1],1); 93 } 94 if(data[l]<n&&pos[data[l]+1]>l) 95 { 96 update(pos[data[l]+1],1); 97 } 98 } 99 res[que[i].xh]=query(que[i].r)-query(que[i].l-1); 100 } 101 for(i=0; i<m; i++) 102 { 103 printf("%d ",res[i]); 104 } 105 } 106 return 0; 107 }
problem 1008(hdu 4639)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4639
Hehe
思路:hehe个数是1,hehehe的个数是2,hehehehe的个数是3。那么表示的意思的种数分别是2,3,5可以推出斐波纳挈数列
将字符串里的统计连续hehe的数量,,再相乘即可
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=10100; 6 const int mod=10007; 7 char str[maxn]; 8 int st[maxn]; 9 void init() 10 { 11 int i; 12 st[0]=0; 13 st[1]=2; 14 st[2]=3; 15 for(i=3;i<=10090;i++) 16 { 17 st[i]=(st[i-2]+st[i-1])%mod; 18 } 19 } 20 int run() 21 { 22 int len=strlen(str); 23 int i; 24 int num=0; 25 int sum=1; 26 for(i=0;i<len-3;) 27 { 28 if(str[i]=='h'&&str[i+1]=='e'&&str[i+2]=='h'&&str[i+3]=='e') 29 { 30 num++; 31 i+=2; 32 } 33 else 34 { 35 if(num!=0) 36 sum=sum*st[num]%mod; 37 num=0; 38 i++; 39 } 40 } 41 if(num!=0) 42 sum=sum*st[num]%mod; 43 return sum; 44 } 45 int main() 46 { 47 int T,k=0; 48 scanf("%d",&T); 49 init(); 50 while(T--) 51 { 52 getchar(); 53 scanf("%s",str); 54 k++; 55 printf("Case %d: %d ",k,run()); 56 } 57 return 0; 58 }
problem 1011(hdu 4642)
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4642
Fliping game
思路:只需要判断map[m][n]是0还是1即可,0则Bob胜,否则Alice胜
因为不管翻动哪一个盒子,总会反动到右下角的那个,当右下角是1时,需翻动奇数次盒子才能翻成0,当右下角是0时,需翻动偶数次盒子才能翻成0
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 int main() 6 { 7 int T; 8 scanf("%d",&T); 9 while(T--) 10 { 11 int n,m; 12 scanf("%d%d",&n,&m); 13 int a; 14 for(int i=0;i<n;i++) 15 { 16 for(int j=0;j<m;j++) 17 { 18 scanf("%d",&a); 19 } 20 } 21 if(a==1) 22 printf("Alice "); 23 else 24 printf("Bob "); 25 } 26 return 0; 27 }