zoukankan      html  css  js  c++  java
  • HNOI2018题解

    在此处输入标题

    标签(空格分隔): 未分类


    重做了一遍,本来以为很快的,结果搞了一天。。。

    寻宝游戏

    可以发现只有(&0)(|1)会对答案有影响

    那么对于每一位,我们只要知道最后一个(&1)和最后一个(|1)谁近就可以了。

    发现并不好做,我们可以把操作串也当成(01)串,如果(&=0,|=1)好像并没有什么用,于是我们令(&=1,|=0)发现这样刚好满足了我们需要的信息,设(op)为操作串,这一位串为(a),如果(op)字典序小于(a)最后会是(1),否则为(0)。((|1=01,&0=10)这就是字典序了,手玩也可以

    那么我们把原串基数排序,那么一定可以重排成(00...011...1),否则无解。如果有解那答案就是第一个(1)串代表的十进数值减掉最后一个(0)串十进制数值。注意下边界条件。

    (code)

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define gt getchar()
    #define ll long long
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    typedef std::pair<int,int> P;
    #define mk std::make_pair
    #define fr first
    #define sc second
    inline int in()
    {
    	int k=0;char ch=gt;bool p=1;
    	while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
    	while(ch>'-')k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    const int YL=1e9+7,N=1005,M=5005;
    inline int ksm(int a,int k){int r=1;while(k){if(k&1)r=1ll*r*a%YL;a=1ll*a*a%YL,k>>=1;}return r;}
    inline int MO(const int &x){return x>=YL?x-YL:x;}
    int pw[M],s[N][M],id[2][M],rk[M],res[M];
    int main()
    {
    	int n=in(),m=in(),q=in();
    	for(int i=1;i<=n;++i)
    	{
    		static char S[M];scanf("%s",S+1);
    		for(int j=1;j<=m;++j)s[i][j]=S[j]-'0';
    	}
    	for(int i=1;i<=m;++i)id[0][i]=i;
    	int now=0;pw[0]=1;
    	for(int i=1;i<=n;++i)
    	{
    		now^=1;int cnt=0,tot=0;pw[i]=MO(pw[i-1]<<1);
    		for(int j=1;j<=m;++j)cnt+=s[i][j]^1;
    		for(int j=1;j<=m;++j)
    			if(s[i][id[now^1][j]])id[now][++cnt]=id[now^1][j];
    			else id[now][++tot]=id[now^1][j];
    	}
    	int *p=id[now];
    	for(int i=1;i<=m;++i)rk[p[i]]=i;
    	for(int i=1;i<=m;++i)
    		for(int j=1;j<=n;++j)
    			res[i]=MO(res[i]+s[j][i]*pw[j-1]);
    	res[m+1]=pw[n];p[m+1]=m+1;
    	while(q--)
    	{
    		static char S[M];scanf("%s",S+1);
    		int mx=-1,mi=m+1;
    		for(int i=1;i<=m;++i)
    			if(S[i]=='0')mx=std::max(mx,rk[i]);
    			else mi=std::min(mi,rk[i]);
    		if(mx>=mi){puts("0");continue;}
    		printf("%d
    ",MO(res[p[mi]]-res[p[mx]]+YL));
    	}
    	return 0;
    }
    
    

    转盘

    可以发现,走一圈是最优的。

    那么即求(min(max(T_j-j)+i)+n-1)

    楼房重建即可

    (code)

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define gt getchar()
    #define ll long long
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    typedef std::pair<int,int> P;
    #define mk std::make_pair
    #define fr first
    #define sc second
    inline int in()
    {
    	int k=0;char ch=gt;bool p=1;
    	while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
    	while(ch>'-')k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    const int N=2e5+5;
    int mis[N<<2],mip[N<<2],mxv[N<<2],a[N];
    #define lc k<<1
    #define rc k<<1|1
    #define ls l, mid ,lc
    #define rs mid+1,r,rc
    #define mid ((l+r)>>1)
    int calc(int mh,int l,int r,int k)
    {
    	if(l==r)return std::max(mh,a[l])+l;int w=mxv[rc];
    	if(mh>=w)return std::min(calc(mh,ls),mh+mid+1);
    	else return std::min(calc(mh,rs),mis[k]);
    }
    inline void up(int l,int r,int k)
    {
    	mxv[k]=std::max(mxv[lc],mxv[rc]);
    	mis[k]=calc(mxv[rc],ls);
    }
    void build(int l,int r,int k)
    {
    	if(l==r)return mxv[k]=a[l],void();
    	build(ls),build(rs),up(l,r,k);
    }
    void upd(int l,int r,int k,int p)
    {
    	if(l==r)return mxv[k]=a[l],void();
    	p<=mid?upd(ls,p):upd(rs,p);up(l,r,k);
    }
    int main()
    {
    	int n=in(),m=in(),op=in(),ans=0;
    	for(int i=1;i<=n;++i)
    		a[i]=a[i+n]=in(),a[i]-=i,a[n+i]-=n+i;
    	build(1,n<<1,1);printf("%d
    ",ans=mis[1]+n-1);
    	for(int i=1;i<=m;++i)
    	{
    		int x=in()^op*ans,y=in()^op*ans;
    		a[x]=y-x;a[x+n]=y-x-n;
    		upd(1,n<<1,1,x),upd(1,n<<1,1,x+n);
    		printf("%d
    ",ans=mis[1]+n-1);
    	}
    	return 0;
    }
    
    

    毒瘤

    之前的博客是我没理解清写的。

    树的(dp)是基础,然后把返祖边们抠出来建虚树,枚举两端情况。

    这里的(f[u][0/1])是表示(u)(0/1)的时候,子树的方案数。

    所以我们只要枚举返祖边的祖先点的状态就可以了。

    然后处理转移系数(xs[u][i=0/1][j=0/1])表示虚树上的父亲选(i),这个点选(j)的系数的转移系数。

    注意到边是有影响的,所以不在虚树上的点的(xs)表示的是该点选(i),这个点子树内第一个虚点选(j)的系数,当我们发现这个(xs)转移到一个虚点时,直接把(xs)挂在后面那维代表的虚点上。

    不在虚树上的点记得乘上这个点选(0/1)的方案数。

    在虚树上的点的(xs)要使得(xs[u][0][0]=xs[u][1][1]=1)

    转移的时候如果这个点被强制选了某个值,另一的(dp)初值必须为(0)

    (code)

    #include<vector>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define pb push_back
    #define gt getchar()
    #define ll long long
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    typedef std::pair<int,int> P;
    #define mk std::make_pair
    #define fr first
    #define sc second
    inline int in()
    {
    	int k=0;char ch=gt;bool p=1;
    	while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
    	while(ch>'-')k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    const int YL=998244353,N=1e5+5;typedef std::vector<int> vi;
    inline int ksm(int a,int k){int r=1;while(k){if(k&1)r=1ll*r*a%YL;a=1ll*a*a%YL,k>>=1;}return r;}
    inline int MO(const int &x){return x>=YL?x-YL:x;}
    vi G[N],E[N];int xs[N][2][2],f[N][2],g[N][2],tsz[N],imp[N],o[N];
    int Eu[N],Ev[N],tot,dep[N],fg[N][2],vis[N],tt;
    void pre_dfs(int u,int pa=0)
    {
    	o[u]=++tt;
    	for(int v:G[u])if(v==pa)continue;
    		else if(!o[v])pre_dfs(v,u),tsz[u]+=tsz[v];
    		else
    		{
    			imp[u]=1;
    			if(o[u]<o[v])
    				Eu[++tot]=u,Ev[tot]=v;
    		}
    	imp[u]|=tsz[u]>=2;tsz[u]=tsz[u]||imp[u];
    }
    void mul(int f[2][2],int g[2][2])
    {
    	int f00=f[0][0],f01=f[0][1];
    	int f10=f[1][0],f11=f[1][1];
    	g[0][0]=MO(f00+f10);
    	g[0][1]=MO(f01+f11);
    	g[1][0]=f00,g[1][1]=f01;
    }
    inline void init(int f[2][2]){f[0][0]=f[1][1]=1,f[0][1]=f[1][0]=0;}
    int dfs(int u)
    {
    	vis[u]=g[u][0]=g[u][1]=1;int pos=0;
    	for(int v:G[u])
    		if(!vis[v])
    		{
    			int w=dfs(v);
    			if(!w)
    			{
    				g[u][0]=1ll*g[u][0]*(g[v][1]+g[v][0])%YL;
    				g[u][1]=1ll*g[u][1]*g[v][0]%YL;
    			}
    			else if(!imp[u])mul(xs[v],xs[u]),pos=w;
    			else mul(xs[v],xs[w]),E[u].pb(w),pos=w;
    		}
    	if(imp[u])return init(xs[u]),u;
    	xs[u][0][0]=1ll*xs[u][0][0]*g[u][0]%YL;
    	xs[u][0][1]=1ll*xs[u][0][1]*g[u][0]%YL;
    	xs[u][1][0]=1ll*xs[u][1][0]*g[u][1]%YL;
    	xs[u][1][1]=1ll*xs[u][1][1]*g[u][1]%YL;
    	return pos;
    }
    void dp(int u)
    {
    	f[u][0]=fg[u][1]?0:g[u][0];
    	f[u][1]=fg[u][0]?0:g[u][1];
    	for(int v:E[u])
    	{
    		dp(v);
    		for(int i=0;i<2;++i)
    			f[u][i]=(1ll*xs[v][i][0]*f[v][0]+1ll*xs[v][i][1]*f[v][1])%YL*f[u][i]%YL;
    	}
    }
    int main()
    {
    	int n=in(),m=in(),ans=0;
    	for(int i=1,u,v;i<=m;++i)
    		u=in(),v=in(),G[u].pb(v),G[v].pb(u);
    	pre_dfs(1),imp[1]=1,dfs(1);int mx=1<<tot;
    	for(int i=0;i<mx;++i)
    	{
    		for(int j=0;j<tot;++j)
    			if(i>>j&1)fg[Eu[j+1]][1]=1,fg[Ev[j+1]][0]=1;
    			else fg[Eu[j+1]][0]=1;
    		dp(1);ans=MO(ans+MO(f[1][1]+f[1][0]));		
    		for(int j=0;j<tot;++j)
    			if(i>>j&1)fg[Eu[j+1]][1]=0,fg[Ev[j+1]][0]=0;
    			else fg[Eu[j+1]][0]=0;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    游戏

    我们发现如果一个点能到(L),且另一个点能到它,那么另一个点肯定能到(L),所以我们预处理(L[i],R[i])(i)点能扩大的最大范围。我们需要安排一个顺序使得他们最优。

    如果一扇门(x,x+1)的钥匙在(1-x)则肯定要先转移(x+1)再转移(x),反之亦然。

    于是我们可以拓扑排序。

    yyb的做法是假的

    问题的关键在于门是不满的,所以有很多没有关系的点之间跳来跳去,复杂度就假了。

    但是由于数据只卡了正着做的,没卡反着做的,于是他的乱搞就能过(他写了两篇乱搞(小声。

    那我们用并查集把没有门的点缩起来就(ok)了。

    (code)

    #include<queue>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define gt getchar()
    #define ll long long
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    typedef std::pair<int,int> P;
    #define mk std::make_pair
    #define fr first
    #define sc second
    inline int in()
    {
    	int k=0;char ch=gt;bool p=1;
    	while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
    	while(ch>'-')k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    const int N=1e6+5;
    int head[N],to[N],du[N],p[N],L[N],R[N],nxt[N],cnt,key[N],tot,n,fa[N];
    inline void add(int u,int v){to[++cnt]=v,nxt[cnt]=head[u],head[u]=cnt,++du[v];}
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    inline void work(int u)
    {
    	int l=L[u],r=R[u],nl,nr;
    	while(1)
    	{
    		nl=l,nr=r;
    		while(l>1&&(!key[l-1]||(l<=key[l-1]&&key[l-1]<=r)))l=L[find(l-1)];
    		while(r<n&&(!key[ r ]||(l<=key[ r ]&&key[ r ]<=r)))r=R[find(r+1)];
    		if(nl==l&&nr==r)break;
    	}
    	L[u]=l,R[u]=r;
    }
    int main()
    {
    	n=in();int m=in(),q=in();
    	for(int i=1,x,y;i<=m;++i)x=in(),y=in(),key[x]=y;
    	for(int i=1;i<=n;++i)fa[i]=i;
    	for(int i=1;i<n;++i)if(!key[i])fa[i+1]=find(i);
    	for(int i=1;i<n;++i)
    		if(key[i])
    		{
    			if(key[i]<=i)add(find(i+1),find(i));
    			else add(find(i),find(i+1));
    		}
    	std::queue<int>Q;
    	for(int i=1;i<=n;++i)if(fa[i]==i&&!du[i])Q.push(i);
    	while(!Q.empty())
    	{
    		int u=p[++tot]=Q.front();Q.pop();
    		for(int i=head[u];i;i=nxt[i])
    			if(!--du[to[i]])Q.push(to[i]);
    	}
    	for(int i=1;i<=n;++i)R[find(i)]=i;
    	for(int i=n;i>=1;--i)L[find(i)]=i;
    	for(int i=1;i<=tot;++i)work(p[i]);
    	while(q--){int x=find(in()),y=find(in());puts(L[x]<=y&&y<=R[x]?"YES":"NO");}
    	return 0;
    }
    
    

    排列

    先把依赖关系的(DAG)建出来。

    然后就是贪心,小的一定要尽量放在前面。

    考虑当前最小值,它在父亲节点删掉后一定会被删。

    所以可以并起来,然后我们现在是考虑一堆序列的顺序,推下式子发现只要平均值小就一定先选,就没了。

    (code)

    #include<cstdlib>
    #include<vector>
    #include<queue>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define gt getchar()
    #define ll long long
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    #define mk std::make_pair
    #define fr first
    #define sc second
    #define double long double
    typedef std::pair<double,int> P;
    inline int in()
    {
    	int k=0;char ch=gt;bool p=1;
    	while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
    	while(ch>'-')k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    typedef std::vector<int> vi;
    const int N=5e5+5;vi G[N];
    const double eps=1e-6;
    int o[N],sz[N],fa[N],ff[N];ll w[N];
    struct Queue
    {
    	std::priority_queue<P>Q1,Q2;
    	void push(P x){Q1.push(x);}
    	void erase(P x){Q2.push(x);}
    	void upd(){while(!Q2.empty()&&Q1.top()==Q2.top())Q1.pop(),Q2.pop();}
    	inline void pop(){upd();Q1.pop();}
    	inline P top(){upd();return Q1.top();}
    }Q;
    int dfs(int u)
    {
    	int ans=u!=0;o[u]=1;
    	for(int v:G[u])
    		if(o[v])puts("-1"),exit(0);
    		else ans+=dfs(v);return ans;
    }
    int find(int x){return x==ff[x]?x:ff[x]=find(ff[x]);}
    int main()
    {
    	int n=in();ll ans=0;
    	for(int i=1;i<=n;++i)G[fa[i]=in()].push_back(i);
    	for(int i=1;i<=n;++i)ans+=w[i]=in();
    	if(dfs(0)!=n)return puts("-1"),0;
    	for(int i=1;i<=n;++i)ff[i]=i,sz[i]=1;
    	for(int i=1;i<=n;++i)Q.push(mk(-(double)w[i],i));
    	for(int i=1;i<=n;++i)
    	{
    		P now=Q.top();Q.pop();int u=find(now.sc),v=find(fa[u]);
    		if(v)Q.erase(mk(-(double)w[v]/sz[v],v));
    		ans+=w[u]*sz[v],w[v]+=w[u],sz[v]+=sz[u],ff[u]=v;
    		if(v)Q.push(mk(-(double)w[v]/sz[v],v));
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    

    道路

    普及(dp),设(F[i][j][k])表示第(i)个城市,没修的公路有(j)条,没修的铁路有(k)条的最小代价。

    叶子节点直接算,非叶子节点枚举修什么。考场上好像卡空间,用分治的(fft)的卡空间技巧就行了

    (code)

    #include<vector>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define gt getchar()
    #define ll long long
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    typedef std::pair<int,int> P;
    #define mk std::make_pair
    #define fr first
    #define sc second
    inline int in()
    {
    	int k=0;char ch=gt;bool p=1;
    	while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0;
    	while(ch>'-')k=k*10+ch-'0',ch=gt;
    	return p?k:-k;
    }
    typedef std::vector<int> vi;
    const int N=20005;vi G[N];
    ll f[N][41][41],a[N],b[N],c[N];
    ll dfs(int u,int L,int R)
    {
    	if(u<0)return c[-u]*(a[-u]+L)*(b[-u]+R);
    	if(~f[u][L][R])return f[u][L][R];int lc=G[u][0],rc=G[u][1];
    	return f[u][L][R]=std::min(dfs(lc,L+1,R)+dfs(rc,L,R),dfs(lc,L,R)+dfs(rc,L,R+1));
    }
    int main()
    {
    	int n=in();
    	for(int i=1;i<n;++i)
    	{
    		int s=in(),t=in();
    		G[i].push_back(s),G[i].push_back(t);
    	}
    	for(int i=1;i<=n;++i)a[i]=in(),b[i]=in(),c[i]=in();
    	memset(f,-1,sizeof f);printf("%lld
    ",dfs(1,0,0));
    	return 0;
    }
    
    
  • 相关阅读:
    解决UITableView中Cell重用机制导致内容出错的方法总结
    Hdu 1052 Tian Ji -- The Horse Racing
    Hdu 1009 FatMouse' Trade
    hdu 2037 今年暑假不AC
    hdu 1559 最大子矩阵
    hdu 1004 Let the Balloon Rise
    Hdu 1214 圆桌会议
    Hdu 1081 To The Max
    Hdu 2845 Beans
    Hdu 2955 Robberies 0/1背包
  • 原文地址:https://www.cnblogs.com/cx233666/p/10651955.html
Copyright © 2011-2022 走看看