hdu4304
一段n*n的墙,用一个有n根毛的刷子来刷,要求刷子的第一根毛在第一个位置,要不重不漏的把墙刷完,求刷子的毛的初始位置的方案数。(被马管子影响用刷子...
考虑用cnt[n,m]表示现在有n段墙,总长度为n*m,用m根毛来刷,有两种转移,一种是n=a*b,那么我们求出cnt[a,m]的可行方案数,之后无非是把这一段重复操作b次,就也可以刷完墙;第二种是m=a*b,考虑我们把每a根毛给并起来,即以a个格子为单位,那么刷子其实就变成了b根毛,总长相应的就变成了n*b,那么求出cnt[n,b]即可。
由于如果连续两次转移都在第一维,那么n/3->n/3/5 也就和第一次直接n/15算重了,因此两种转移要交替进行,那么进一步观察这个过程,实际上就是将n和m交替进行因式分解,不能分出1有多少种方案。
于是,用f[i][j]来表示第i个约数,分解了j个数出来的方案数,这个dp出来后,答案就很好算了,同时由于2^40>10^12,拥有2000个约数的最小数是10^12级别的,这两维都不会特别大,然后每个约数压缩成各个因子的个数来表示的话,就不需要枚举约数进行转移,但是感觉也不是很小。主代码手说可以2000*40*40不知道是不是这个转移方法。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int Lim=1000000;
using namespace std;
int t,N,M,cnt[2050],st[2050],prim[2000000],tot,b[2050];
long long n,f[2050][55];
bool check[2000000];
void origin()
{
tot=0;
for (int i=2;i<=Lim;i++) {
if (!check[i]) prim[++tot]=i;
for (int j=1;j<=tot;j++) {
if (i*prim[j]>Lim) break;
check[i*prim[j]]=1;
if (i%prim[j]==0) break;
}
}
}
void dfs(int x,int s,int i,int j,int d)
{
if (x<1) {
if (s==i) return ;
f[s][j+1]+=f[i][j];
return ;
}
for (int k=b[x];k<=cnt[x];k++)
dfs(x-1,s+(k-b[x])*d,i,j,d*(cnt[x]+1));
}
int main()
{
freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
scanf("%d",&t);
origin();
for (;t;t--) {
//scanf("%I64d",&n);
scanf("%lld",&n);
N=0;
long long x=n;
for (int i=1;i<=tot && x!=1;i++) {
if (x%prim[i]==0) st[++N]=prim[i],cnt[N]=0;
for (;x!=1 && (x%prim[i]==0);x/=prim[i])
cnt[N]++;
}
if (x!=1) st[++N]=x,cnt[N]=1;
int lim=0,siz=0;
for (int i=1;i<=N;i++)
lim=lim*(cnt[i]+1)+cnt[i],siz+=cnt[i];
for (int i=0;i<=lim;i++)
for (int j=0;j<=siz+1;j++)
f[i][j]=0;
f[0][0]=1;
for (int i=0;i<=lim;i++)
for (int j=0;j<=siz;j++)
if (f[i][j]) {
int s=i;
for (int k=N;k>=1;k--) {
b[k]=s%(cnt[k]+1);
s/=cnt[k]+1;
}
dfs(N,i,i,j,1);
}
long long ans=0;
for (int j=1;j<=siz;j++)
ans+=f[lim][j]*f[lim][j]*2+f[lim][j]*f[lim][j+1]*2;
if (1==n) ans=1;
printf("%lld\n",ans);
}
return 0;
}
hdu4305
生成树计数
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define sqr(x) ((x)*(x))
const int mo=10007;
using namespace std;
struct point{
int x,y,d;
}p[500];
int d[500][500],f[500][500],A[500][500],D[500],u[500],c[500];
int ny[mo+5];
int n,R,t;
int dist(point p,point q)
{
return sqr(p.x-q.x)+sqr(p.y-q.y);
}
int swap(int a[],int b[],int i,int j,int n)
{
if (i==j) return 1;
for (int k=1;k<=n;k++) c[k]=a[k];
for (int k=1;k<=n;k++) a[k]=b[k];
for (int k=1;k<=n;k++) b[k]=c[k];
return ((j-i) & 1) ? -1 : 1;
}
int solve(int f[500][500],int n)
{
int j=1,e=1;
for (int i=1;i<=n;i++) {
int k=j;
for (;k<=n && !f[k][i];k++) ;
if (k<=n) {
e*=swap(f[j],f[k],j,k,n);
for (k++;k<=n;k++)
if (f[k][i]) {
int p=f[k][i],q=f[j][i];
e=((e*ny[q])%mo+mo)%mo;
for (int l=i;l<=n;l++)
f[k][l]=((f[k][l]*q-f[j][l]*p)%mo+mo)%mo;
}
j++;
}
}
for (int i=1;i<=n;i++)
e=((e*f[i][i])%mo+mo)%mo;
return e;
}
bool check(int i,int j,int k)
{
return (p[j].x-p[i].x)*(p[k].x-p[i].x)+(p[j].y-p[i].y)*(p[k].y-p[i].y)>0 && ((p[j].x-p[i].x)*(p[k].y-p[i].y)==(p[j].y-p[i].y)*(p[k].x-p[i].x));
}
bool cmp(int i,int j)
{
return p[i].d<p[j].d;
}
int main()
{
freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
scanf("%d",&t);
ny[1]=1;
for (int i=2;i<mo;i++)
ny[i]=((-(mo/i)*ny[mo%i])%mo+mo)%mo;
for (;t;t--) {
scanf("%d%d",&n,&R);
R*=R;
for (int i=1;i<=n;i++) scanf("%d%d",&p[i].x,&p[i].y);
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
d[i][j]=d[j][i]=dist(p[i],p[j]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
A[i][j]=0;
for (int i=1;i<=n;i++) {
for (int j=1;j<=n;j++)
p[j].d=d[i][j];
for (int j=1;j<=n;j++) u[j]=j;
sort(u+1,u+n+1,cmp);
for (int j=1;j<=n;j++)
if (u[j]!=i) {
if (d[i][u[j]]>R) break;
if (A[i][u[j]]!=0) continue;
bool flag=1;
for (int k=1;k<j;k++)
if (u[k]!=i)
if (check(i,u[k],u[j])) {
flag=0;
break;
}
A[i][u[j]]=A[u[j]][i]=flag;
}
}
for (int i=1;i<=n;i++) {
D[i]=0;
for (int j=1;j<=n;j++)
D[i]+=A[i][j];
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (i!=j) f[i][j]=-A[i][j];
else f[i][j]=D[i];
int ans=solve(f,n-1);
if (!ans) ans=-1;
printf("%d\n",ans);
}
return 0;
}
hdu4701
考虑设f[i]为i~n先手必胜先手所需最少钱数,那么f[n]就为c[n],而对于f[i-1],要么是买了c[i-1]继续准备买之后的东西,即最小需要f[i]+c[i-1]的钱数,要么是买了c[i-1]之后逼迫后手陷入必败局,那么i~n的总剩余钱数就为A+B-s[i-1],而对手必败时的最大钱数即为f[i]-1,那么自己所持有的最小钱数即为A+B-s[i-1]-(f[i]-1),同时还要加上c[i-1]的代价。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,A,B,c[2000000],s[2000000],f[2000000];
int main()
{
freopen("hdu4701.in","r",stdin);
freopen("hdu4701.out","w",stdout);
for (;scanf("%d%d%d",&n,&A,&B)==3;) {
for (int i=1;i<=n;i++)
scanf("%d",&c[i]);
if (c[1]>A) {
printf("BOB\n");
continue;
}
s[0]=0;
for (int i=1;i<=n;i++) {
s[i]=s[i-1]+c[i];
if (s[i]>A+B) {
n=i-1;
break;
}
}
f[n]=c[n];
for (int i=n-1;i>=1;i--)
f[i]=min(f[i+1]+c[i],A+B-s[i]-(f[i+1]-1)+c[i]);
// cout<<n<<' '<<f[1]<<' '<<f[2]<<endl;
if (f[1]<=A) {
printf("ALICE\n");
}
else printf("BOB\n");
}
return 0;
}
hdu4322
卡状压简直是坑...用费用流处理出需要用喜欢的糖的情况,而对不喜欢的糖,其实大家都是一样的,可以直接一起考虑。
对于b[i]%k==0时是不需要建边的,因为多流一条过去反而可能使答案变差。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
const int oo=1073741819;
using namespace std;
int tail[250],d[250],v[250],flag[250],b[1050],p[250],P[250],c[250][250],B[250];
int next[150000],sora[150000],flow[150000],cost[150000],st[150000],po[150000];
int n,ss,s,t,phi,ans,sum,w_time,e,m1,m,k,T;
void change(int x,int w)
{
d[x]=w;
for (x=((x+m1)>>1);x;x>>=1)
if (d[b[x<<1]]>d[b[(x<<1)^1]]) b[x]=b[(x<<1)^1];
else b[x]=b[x<<1];
}
bool spfa(int s,int t)
{
int ne,na;
for (int i=0;i<=t;i++) d[i]=p[i]=oo;
p[t]=0;
change(t,p[t]);
for (;d[b[1]]!=oo;) {
ne=b[1];
change(ne,oo);
for (int i=ne;next[i];) {
i=next[i],na=sora[i];
if (flow[po[i]] && p[ne]+cost[po[i]]+P[ne]<p[na]+P[na]) {
p[na]=p[ne]+cost[po[i]]+(P[ne]-P[na]);
change(na,p[na]);
}
}
}
if (p[s]>=oo) return 0;
for (int i=1;i<=t;i++)
if (p[i]<oo) P[i]+=p[i];
return 1;
}
int dfs(int x,int low)
{
if (t==x) {
ans+=P[s]*low;
return low;
}
int sum=0,tmp,ne;
flag[x]=w_time;
for (int i=x;next[i];) {
i=next[i],ne=sora[i];
if (flow[i] && flag[ne]!=w_time && cost[i]+P[ne]==P[x]) {
if (flow[i]<low) tmp=dfs(ne,flow[i]);
else tmp=dfs(ne,low);
flow[i]-=tmp,flow[po[i]]+=tmp,sum+=tmp,low-=tmp;
if (!low) break;
}
}
return sum;
}
void origin()
{
s=n+m+1,t=s+1,ss=t+1;
for (int i=1;i<=t;i++) tail[i]=i,next[i]=0;
for (m1=1;m1<t;m1<<=1) ;m1--;
for (int i=1;i<=t;i++) b[i+m1]=i;
}
void link(int x,int y,int z,int c)
{
++ss,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,flow[ss]=z,cost[ss]=c,next[ss]=0;
++ss,next[tail[y]]=ss,tail[y]=ss,sora[ss]=x,flow[ss]=0,cost[ss]=-c,next[ss]=0;
po[ss]=ss-1,po[ss-1]=ss;
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&T);
for (int test=1;T;T--,test++) {
printf("Case #%d: ",test);
scanf("%d%d%d",&n,&m,&k);
origin();
for (int i=1;i<=m;i++) {
scanf("%d",&B[i]);
link(s,i,B[i]/k,-k);
if (B[i]%k!=0) link(s,i,1,-B[i]%k);
}
for (int i=1;i<=m;i++)
for (int j=1;j<=n;j++) {
scanf("%d",&c[i][j]);
if (c[i][j]) link(i,j+m,1,0);
}
for (int i=1;i<=n;i++)
link(i+m,t,1,0);
ans=0,sum=0;
for (int i=0;i<=t;i++) P[i]=p[i]=0;
for (;spfa(s,t);)
for (int tmp;w_time++,tmp=dfs(s,oo);) sum+=tmp;
int tot=0;
for (int i=1;i<=m;i++) tot+=B[i];
tot+=ans;
//cout<<tot<<' '<<ans<<' '<<sum<<endl;
if (tot>(n-sum)) printf("NO\n");
else printf("YES\n");
}
return 0;
}
hdu3546
这是逼我学java用高精...小朋友们写两个高精写了整场...
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
static BigInteger ans[];
public static void main(String args[]) {
Scanner cin=new Scanner(new BufferedInputStream(System.in));
ans=new BigInteger[13];
for (int i=0;i<13;i++) ans[i]=ans[i].ONE;
String str;
while (cin.hasNext()) {
str=cin.next();
if (str.charAt(1)=='=')
ans[str.charAt(0)-'A']=ans[str.charAt(2)-'A'];
else if (str.charAt(1)=='+')
ans[str.charAt(0)-'A']=ans[str.charAt(0)-'A'].add(ans[str.charAt(3)-'A']);
else if (str.charAt(1)=='*')
ans[str.charAt(0)-'A']=ans[str.charAt(0)-'A'].multiply(ans[str.charAt(3)-'A']);
}
for (int i=0;i<10;i++)
System.out.println(ans[i]);
}
}
hdu3547
polya
sigma(c^(循环节个数))/置换个数
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
static BigInteger ans;
public static void main(String args[]) {
Scanner cin=new Scanner(new BufferedInputStream(System.in));
int t=cin.nextInt();
for (int test=1;test<=t;test++) {
System.out.printf("Case %d: ",test);
int c=cin.nextInt();
BigInteger d=BigInteger.valueOf(c);
ans=BigInteger.ZERO;
for (int i=1;i<=6;i++)
ans=ans.add(d.pow(2));
for (int i=1;i<=17;i++)
ans=ans.add(d.pow(4));
for (int i=1;i<=1;i++)
ans=ans.add(d.pow(8));
ans=ans.divide(BigInteger.valueOf(24));
if (ans.compareTo(BigInteger.valueOf(1000000000000000L))==1) {
ans=ans.remainder(BigInteger.valueOf(1000000000000000L));
System.out.printf("%015d",ans);
System.out.println();
}
else {
System.out.println(ans);
}
}
}
}
hdu3553
小朋友们卡高精过不了,于是乎忍不住冲上去写了个sam,结果wa在long long的读入上,后来不敢继续查还以为是我写错了...
询问可重第k小子串,因为询问可重第k小,所以不能直接求,对于每个节点,要通过rt边知道他被多少个后缀包含,就知道了他接受的子串会出现多少次,然后再用转移边dp出每个节点代表的子串会有多少串以它为前缀,就可以直接走出来,就跟去重的第k小子串一样了。
感觉sam上每个节点代表的子串有很多性质都是相同的,所以才会被用一个节点表示。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <utility>
using namespace std;
map < char , int > next[200000];
int l[200000],rt[200000];
long long f[200000],k,g[200000];
int s1,len,t,u[200000],v[200000];
char ch[200000];
void suffix_sam(int &last,char chr)
{
int x,y;
s1++,l[s1]=l[last]+1,v[s1]=1;
for (x=last,last=s1;x && (!next[x].count(chr));x=rt[x]) next[x][chr]=s1;
if (!next[x].count(chr)) next[x][chr]=s1,rt[s1]=0;
else {
y=next[x][chr];
if (l[x]+1==l[y]) rt[s1]=y;
else {
s1++,l[s1]=l[x]+1;
next[s1]=next[y];
rt[s1]=rt[y],rt[y]=s1,rt[last]=s1;
for (;x && (next[x].count(chr) && next[x][chr]==y);x=rt[x]) next[x][chr]=s1;
if (next[x].count(chr) && next[x][chr]==y) next[x][chr]=s1;
}
}
}
void origin()
{
for (int i=0;i<=s1;i++)
next[i].clear(),v[i]=0;
s1=0;
}
bool cmp(int i,int j)
{
return l[i]<l[j];
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&t);
for (int test=1;t;t--,test++) {
printf("Case %d: ",test);
scanf("%s",ch+1);
scanf("%lld",&k);
origin();
len=strlen(ch+1);
int last=0;
for (int i=1;i<=len;i++) suffix_sam(last,ch[i]);
for (int i=1;i<=s1;i++) u[i]=i,f[i]=0,g[i]=0;
sort(u+1,u+s1+1,cmp);
for (int i=s1;i>=1;i--) {
int ne=u[i];
if (v[ne]) g[ne]++;
g[rt[ne]]+=g[ne];
f[ne]=g[ne];
map < char , int >:: iterator it=next[ne].begin();
for (;it!=next[ne].end();it++) {
int na=it->second;
f[ne]+=f[na];
}
}
for (int s=0;k>0;) {
map < char , int > :: iterator it=next[s].begin();
for (;it!=next[s].end();it++) {
int ne=it->second;
if (k>f[ne]) k-=f[ne];
else {
k-=g[ne];
printf("%c",it->first);
s=ne;
break;
}
}
}
printf("\n");
}
return 0;
}