【除草】一些题目

35 篇文章 0 订阅
24 篇文章 0 订阅

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值