2018HDU多校训练3

题目链接:https://cn.vjudge.net/contest/299607

Problem A. Ascending Rating

HDU - 6319
题解转载自:https://blog.csdn.net/qq_36258516/article/details/81290393
题意:先给出一个长度为k(k<=n)的序列,和一个递推式,让你先求出整个长度为n的序列,然后对于每个区间都求题目给的式子,其中maxratingi为区间最大值,count为区间上升序列元素个数。对于不同的区间maxratingi和count要重新算。
题解:递推区间的最大值可以用单调队列写,POJ2823跟这个一样,复杂度是O(n),比赛的时候也想到了。但是总是想不出count如何用O(n)的复杂度求出来,开始也想过单调队列,但是没有反着想。结果这题的正解是反着用单调队列,维护一个递减的序列,对于每个区间的count就是队尾-队首的值。
吐槽:此题卡时间好紧,少一个取模不对,多一个取模TLE…

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<unordered_map>
#include<map>
using namespace std;
#define ll long long
const int maxn=10000010;

int n,a[maxn];
int dq[maxn],head,rear;
int m,k,p,q,r,mod;
int mx[maxn],num[maxn];

void getmx()
{
    head=rear=0;
    for(int i=1;i<=n;i++){
        while(head<rear&&a[dq[rear-1]]<=a[i]) rear--;
        dq[rear++]=i;
        while(head<rear&&dq[head]<i-m+1) head++;
        mx[i]=a[dq[head]];
    }
}
void getnum()
{
    head=rear=0;
    for(int i=n;i>=1;i--){
        while(head<rear&&a[dq[rear-1]]<=a[i]) rear--;
        dq[rear++]=i;
        if(i>n-m+1) continue;
        while(head<rear&&dq[head]>i+m-1) head++;
        num[i]=rear-head;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
        for(int i=1;i<=k;i++){
            scanf("%d",&a[i]);
        }
        for(int i=k+1;i<=n;i++){
            a[i]=(((ll)p%mod*a[i-1]%mod+(ll)q%mod*i%mod)+r%mod)%mod;// a[i-1]%mod (i%mod) %mod
            //a[i]=((ll)p%mod*a[i-1]%mod+(ll)q%mod*i%mod+r%mod)%mod;//
        }
        getmx();
        getnum();
        ll A=0,B=0;
        for(int i=1;i<=n-m+1;i++){
            A+=1LL*(mx[i+m-1]^i);
            B+=1LL*(num[i]^i);
        }
        printf("%lld %lld\n",A,B);
    }
    return 0;
}

Problem B. Cut The String

HDU - 6320
代码来源:https://cn.vjudge.net/status/#un=&OJId=HDU&probNum=6320&res=1&language=&onlyFollowee=false

#include<bits/stdc++.h>
using namespace std;
const int maxn=100020;

int tot,n,q;
char st[maxn];
struct eda{
	int a,d,n;
	eda(int a):a(a),d(0),n(0){}
};
struct PAM{
	int n,tot,last;
	int len[maxn],fail[maxn],nxt[maxn][26];
	vector<eda> a[maxn];
	void init(){
		for(int i=0;i<tot;i++){
			len[i]=fail[i]=0;
			for(int j=0;j<26;j++) nxt[i][j]=0;
			a[i].clear();
		}
		n=0;
		tot=1;
		len[1]=-1;fail[1]=0;
		len[0]=0;fail[0]=1;
		last=1;
	}
	int get_fail(int x){
		for(;st[n-len[x]-1]!=st[n];x=fail[x]);
		return x;
	}
	void insert(char c){
		++n;
		int cur=get_fail(last);
		if(!nxt[cur][c]){
			++tot;
			len[tot]=len[cur]+2;
			fail[tot]=nxt[get_fail(fail[cur])][c];
			nxt[cur][c]=tot;
		}
		last=nxt[cur][c];
		a[last]=a[fail[last]];
		int ok=0;
		if(a[last].size()){
			eda &t=a[last][a[last].size()-1];
			if(t.n==0){
				t.d=len[last]-t.a;
				t.n=1;
				ok=1;
			}else if(t.a+t.d*(t.n+1)==len[last]){
				++t.n;
				ok=1;
			}
		}
		if(!ok) a[last].push_back(eda(len[last]));
	}
}pam;
vector<eda> f[maxn],g[maxn];
int exgcd(int a,int b,int &x,int &y)
{
	if(!b) return x=1,y=0,a;
	int d=exgcd(b,a%b,x,y),t=x;
	return x=y,y=t-a/b*y,d;
}
bool debug;
int main()
{
	scanf("%d",&tot);
	while(tot--){
		scanf("%d%d",&n,&q);
		scanf("%s",st+1);
		pam.init();
		for(int i=1;i<=n;i++){
			pam.insert(st[i]-'a');
			g[i]=pam.a[pam.last];
		}
		reverse(st+1,st+n+1);
		pam.init();
		for(int i=1;i<=n;i++){
			pam.insert(st[i]-'a');
			f[n+1-i]=pam.a[pam.last];
		}
		while(q--){
			int l,r;
			scanf("%d%d",&l,&r);
			const vector<eda> &v1=f[l];
			const vector<eda> &v2=g[r];
			int len=r-l+1;
			int ans=0;
			for(int i=0;i<(int)v1.size();i++)
				for(int j=0;j<(int)v2.size();j++)
					if(v1[i].a+v2[j].a<=len&&len<=v1[i].a+v1[i].d*v1[i].n+v2[j].a+v2[j].d*v2[j].n){
						int c=len-v1[i].a-v2[j].a;
						int a=v1[i].d;
						int b=v2[j].d;
						if(!a&&!b){
							ans+=c==0;
							continue;
						}
						if(!a){
							if(c%b) continue;
							if(c/b<=v2[j].n) ans+=1;
							continue;
						}
						if(!b){
							if(c%a) continue;
							if(c/a<=v1[i].n) ans+=1;
							continue;
						}
						int x,y;
						
						int g=exgcd(a,b,x,y);						
						if(c%g) continue;
						x*=c/g;
						y*=c/g;
						int x1=0,x2=v1[i].n;
						int y1=0,y2=v2[j].n;
						int dx=b/g;
						int dy=a/g;
						int t;						
						if(x>0){							
							t=x/dx+1;
							x=x-t*dx;
							y=y+t*dy;
						}						
						t=(-x)/dx;
						if((-x)%dx) ++t;
						x=x+t*dx;
						y=y-t*dy;
						x1=max(x1,x);
						y2=min(y2,y);
						if(x<v1[i].n){
							t=(v1[i].n-x)/dx+1;
							x=x+t*dx;
							y=y-t*dy;
						}					
						t=(x-v1[i].n)/dx;
						if((x-v1[i].n)%dx) ++t;
						x=x-t*dx;
						y=y+t*dy;
						x2=min(x2,x);
						y1=max(y1,y);
						
						if(y>0){
							t=y/dy+1;
							x=x+t*dx;
							y=y-t*dy;
						}
						t=(-y)/dy;
						if((-y)%dy) ++t;
						x=x-t*dx;
						y=y+t*dy;
						y1=max(y1,y);
						x2=min(x2,x);
						if(y<v2[j].n){
							t=(v2[j].n-y)/dy+1;
							x=x-t*dx;
							y=y+t*dy;
						}
						t=(y-v2[j].n)/dy;
						if((y-v2[j].n)%dy) ++t;
						x=x+t*dx;
						y=y-t*dy;
						y2=min(y2,y);
						x1=max(x1,x);
						
						if(x1>x2&&y1>y2) continue;
						assert(x2-x1==y2-y1);
						ans+=x2-x1+1;
					}
			printf("%d\n",ans);
		}
	}
}

Problem C. Dynamic Graph Matching

HDU - 6321
题解转载自:https://www.cnblogs.com/xiuwenli/p/9398342.html
题意:给定一个N个点的零图,M次操作,添加或删除一条边,每一次操作以后,打印用1,2,…N/2条边构成的匹配数。
分析:因为N的范围很小,所以可以把点的枚举状态用二进制表示集合。用一维数组dp[S]表示二进制集合为S的点集的匹配数。
每次加边操作,从大到小遍历集合,dp[S]+=dp[S-u-v];删边操作,从小到大遍历集合,dp[S]-=dp[S-u-v]。
预处理出每个1024之内每个数对应二进制含有1的个数,每次记录答案就将每个dp[S]加到ans[S]对应的二进制个数]中。

_builtin_popcount()计算二进制中多少个1

#include<bits/stdc++.h>
using namespace std;
const int maxn =1030;
const int mod = 1e9+7;
typedef long long LL;
void add(int &a,int b){a=a+b<mod?a+b:a+b-mod;}
void del(int &a,int b){a=a-b<0?a-b+mod:a-b;}
int dp[maxn],ans[15],cnt[maxn];

int main()
{
    #ifndef ONLINE_JUDGE
         freopen("in.txt","r",stdin);
         freopen("out.txt","w",stdout);
    #endif
    int T,N,M,u,v;
    char op[5];
    scanf("%d",&T); 
    while(T--){
        scanf("%d%d",&N,&M);
        int tot=1<<N;
        for(int i=0;i<tot;++i){
            dp[i]=0;
            cnt[i] = __builtin_popcount(i);
        }
        dp[0]=1;
        while(M--){
            scanf("%s%d%d",op,&u,&v);
            memset(ans,0,sizeof(ans));
            u--,v--;
            int S = (1<<u)|(1<<v);                  //取只包含u,v的集合
            if(op[0]=='+'){
                for(int t=tot-1;~t;--t)
                    if(!(t&S)) add(dp[t^S],dp[t]);  //加上原来不包含的u,v的集合的匹配数
            }
            else{
                for(int t=0;t<tot;++t)
                    if(!(t&S)) del(dp[t^S],dp[t]);  //减去原来不包含u,v的集合的匹配数
            }
            for(int i=1;i<tot;++i) add(ans[cnt[i]],dp[i]);
            for(int i=2;i<=N;i+=2) printf("%d%c",ans[i],i<N?' ':'\n');
        }
    }
    return 0;
}

Problem D. Euler Function

HDU - 6322
打表找规律

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int k;
        scanf("%d", &k);
        if (k == 1)
            printf("5\n");
        else
            printf("%d\n", k + 5);
    }
    return 0;
}

Problem E. Find The Submatrix(斜率dp)

HDU - 6323
题解转载自:https://blog.csdn.net/qkoqhh/article/details/84107763
题意:给定一个n*m的矩阵,可以在矩阵内将A个元素改成0,现在要在矩阵内选出B个不相交的宽为m的矩阵,使得这3个矩阵的和最大这个可以将所有行压成一个点然后变成序列上的问题,
修改可以预处理,显然对每行来说,应优先修改最小的元素,那么就可以预处理出a[i][j],代表第i行修改j次后的权值。显然修改的效果会越来越小,所以 f ( j ) = a [ i ] [ j ] f(j)=a[i][j] f(j)=a[i][j]是一个上凸函数。。
然后设 d [ p ] [ i ] [ j ] d[p][i][j] d[p][i][j]为到第i行,选了p个矩形(第p个矩形以第i行结尾),修改了j次的最大值
那么
d [ p ] [ i ] [ j ] = m a x ( m a x ( d [ p ] [ i − 1 ] [ k ] + a [ i ] [ j − k ] ) , m a x ( d [ p − 1 ] [ v ] [ k ] + a [ i ] [ j − k ] ) ) d[p][i][j]=max(max(d[p][i-1][k]+a[i][j-k]),max(d[p-1][v][k]+a[i][j-k])) d[p][i][j]=max(max(d[p][i1][k]+a[i][jk]),max(d[p1][v][k]+a[i][jk]))
= m a x ( m a x ( d [ p ] [ i − 1 ] [ k ] , d [ p − 1 ] [ v ] [ k ] ) + a [ i ] [ j − k ] ) =max(max(d[p][i-1][k],d[p-1][v][k])+a[i][j-k]) =max(max(d[p][i1][k],d[p1][v][k])+a[i][jk])
那么设
g [ p ] [ i ] [ k ] = m a x ( d [ p ] [ i ] [ k ] , d [ p − 1 ] [ v ] [ k ] ) g[p][i][k]=max(d[p][i][k],d[p-1][v][k]) g[p][i][k]=max(d[p][i][k],d[p1][v][k])
d [ p ] [ i ] [ j ] = m a x ( g [ p ] [ i ] [ k ] + a [ i ] [ j − k ] ) d[p][i][j]=max(g[p][i][k]+a[i][j-k]) d[p][i][j]=max(g[p][i][k]+a[i][jk])
复杂度是O(TnABm),
显然会T而由于a的性质比较特殊,所以这个决策具有单调性对于2个决策点k<v<j,
如果 g [ p ] [ i ] [ k ] + a [ i ] [ j − k ] &lt; g [ p ] [ i ] [ v ] + a [ i ] [ j − v ] g[p][i][k]+a[i][j-k]&lt;g[p][i][v]+a[i][j-v] g[p][i][k]+a[i][jk]<g[p][i][v]+a[i][jv]
由于 a [ i ] [ x ] a[i][x] a[i][x]为上凸函数,所以随着j增加,v一直比k优,因此可以用单调队列维护决策,用二分找到决策区间就可以了。。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mid (x+y>>1)
#define NM 105
#define nm 10005 

#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))


const ll inf=1e9+7;
ll read(){
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}
 
int q[nm],qh,qt,b[NM],n,tot,m,_p,v[nm],f[nm];
ll a[NM][3005],g[NM][nm],d[NM][nm],ans;
 
int main(){
    int _=read();while(_--){
		n=read();tot=read();m=read();_p=read();
		inc(i,1,n){
		    a[i][0]=0;
		    inc(j,1,tot)b[j]=read(),a[i][0]+=b[j];
		    sort(b+1,b+tot+1);
		    inc(j,1,tot)a[i][j]=a[i][j-1]+max(0,-b[j]);
		}
		//inc(i,1,n){inc(j,0,tot)printf("%lld ",a[i][j]);putchar('\n');}
		mem(g);ans=0;
		inc(i,1,n)inc(j,0,m)d[i][j]=-inf;
		inc(p,1,_p){
		    inc(i,1,n){
				q[qh=qt=1]=0;mem(f);mem(v);
				d[i][0]=max(d[i][0],g[i-1][0]+a[i][0]);
				g[i][0]=max(g[i][0],d[i][0]);
				inc(j,1,m){
				    f[j]=max(f[j],f[j-1]);
				    while(qh<=qt&&f[j]!=q[qh])qh++;
				    int s=j;
				    while(qh<=qt){
					s=q[qt]+tot+1;
					for(int x=j,y=min(m,q[qt]+tot);x<=y;)
					    if(g[i-1][q[qt]]+a[i][mid-q[qt]]<=g[i-1][j]+a[i][mid-j])s=mid,y=mid-1;else x=mid+1;
					if(s<=v[q[qt]])qt--;else break;
				    }
				    if(s<=m)v[j]=s,f[s]=j,q[++qt]=j;
				    while(qh<=qt&&f[j]!=q[qh])qh++;
				    //printf("%d %d:%d\n",i,j,q[qh]);
				    d[i][j]=max(d[i][j],g[i-1][q[qh]]+a[i][j-q[qh]]);
				    g[i][j]=max(g[i][j],d[i][j]);
				}
		    }
		    //inc(i,1,n){inc(j,0,m)printf("%lld ",d[i][j]);putchar('\n');}
		    inc(i,1,n)inc(j,0,m)g[i][j]=max(g[i][j],g[i-1][j]);
		}
		inc(j,0,m)ans=max(ans,g[n][j]);
		printf("%lld\n",ans);
    }
    return 0;
}

Problem F. Grab The Tree

HDU - 6324
题意:Q与T玩游戏,在一棵树上每个节点都有一个权值,Q能取任意个节点,但是这些节点不能相连,剩下的都归T。比较两个人节点权值全部的异或和,如果相同则输出D,否则输出Q或T
题解:
如果A^B=C, 则有A=B^C
A^B=C 则C<=max(A,B)
利用以上性质,可知,如果所有点的异或和S为0,则平局,否则一定是Q赢

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
const int maxn=100010;

int n;
int w[maxn];
int res;

int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		res=0;
		for(int i=1;i<=n;i++){
			scanf("%d",&w[i]);
			res^=w[i];
		}
		int u,v;
		for(int i=1;i<n;i++){
			scanf("%d%d",&u,&v);
		} 
		
		if(!res)
			printf("D\n");
		else
			printf("Q\n");
	}
	return 0;
}

Problem G. Interstellar Travel

HDU - 6325
题解转载自:https://blog.csdn.net/liufengwei1/article/details/81298610
给你n个点,第一个点一定是(0,0),最后一个点纵坐标yn一定是0,中间的点的横坐标一定都是在(0,xn)之间的 然后从第一个点开始飞行,每次飞到下一个点j,你花费的价值就是 x i ∗ y j − x j ∗ y i xi*yj-xj*yi xiyjxjyi,并且这里每一次飞行必须满足 x i &lt; x j xi&lt;xj xi<xj 让你求出你所花费的最小的价值(可以为负)下,飞行的路径,如果有多种情况,输出路径字典序最小的那个

只要把那个式子看出来是叉积,然后再联想到叉积之和就是面积大小,就可以知道最小的cost就是上凸包最大的面积,都不用什么特殊的做法,首先相同x坐标,必取y大的,上凸包的拐点必取,然后同斜率上一条线上的点,从后往前维护ind的最小值,如果当前的ind比他后面的点ind更小,那么比取,以得到最小的字典序。值得注意的是,对于x,y相同的点,必取ind小的

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxl 200010
 
using namespace std;
 
int n,top;
struct node
{
	int x,y,ind;
}a[maxl],s[maxl];
int ans[maxl],f[maxl];
bool in[maxl];
 
inline bool cmp(const node &x,const node &y)
{
	if(x.x!=y.x)
		return x.x<y.x;
	if(x.y!=y.y)
		return x.y>y.y;
	return x.ind<y.ind;
}
 
inline void prework()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].x,&a[i].y),a[i].ind=i;
	sort(a+1,a+1+n,cmp);
}
 
inline void mainwork()
{
	top=0;
	for(int i=1;i<=n;i++)
	{
		if(i>1 && a[i].x==a[i-1].x) continue;
		while(top>1 && 1ll*(s[top].y-s[top-1].y)*(a[i].x-s[top].x)<1ll*(a[i].y-s[top].y)*(s[top].x-s[top-1].x))
			top--;
		s[++top]=a[i];
	}
	for(int i=1;i<=n;i++)
		in[i]=false,f[i]=0;
	in[1]=true;f[1]=s[1].ind;in[top]=true;f[top]=s[top].ind;
	for(int i=top-1;i>=1;i--)
	if(1ll*(s[i].y-s[i-1].y)*(s[i+1].x-s[i].x)!=1ll*(s[i+1].y-s[i].y)*(s[i].x-s[i-1].x))
		in[i]=true,f[i]=s[i].ind;
	for(int i=top-1;i>=2;i--)
	if(!in[i])
		f[i]=min(s[i].ind,f[i+1]);
	ans[0]=0;
	for(int i=1;i<=top;i++)
	if(f[i]==s[i].ind)
		ans[++ans[0]]=f[i];
}
 
inline void print()
{
	for(int i=1;i<ans[0];i++)
		printf("%d ",ans[i]);
	printf("%d\n",ans[ans[0]]);
}
 
int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}
 

Problem H. Monster Hunter(贪心+优队)

HDU - 6326
题解转载自:https://blog.csdn.net/V5ZSQ/article/details/82377490
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define maxn 100005
struct node
{
    ll a,b;
    int id;
    node(){};
    node(ll _a,ll _b,int _id)
    {
        a=_a,b=_b,id=_id;
    }
    bool operator<(const node&x)const
    {
        if(a-b<=0&&x.a-x.b<=0)return a>x.a;
        if(a-b>0&&x.a-x.b>0)return b<x.b;
        return a-b>x.a-x.b;
    }
}m[maxn];
node merge(node x,node y)
{
    return node(x.a+max(y.a-x.b,0ll),y.b+max(x.b-y.a,0ll),y.id);
}
priority_queue<node>que;
int T,n,fa[maxn],pa[maxn],vis[maxn];
vector<int>g[maxn];
void dfs(int u,int f)
{
    pa[u]=f;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==f)continue;
        dfs(v,u);
    }
}
int find(int x)
{
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
        {
            scanf("%lld%lld",&m[i].a,&m[i].b);
            m[i].id=i;
            que.push(m[i]);
        }
        for(int i=1;i<=n;i++)g[i].clear(),vis[i]=0,fa[i]=i;
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v),g[v].push_back(u);
        }   
        dfs(1,1);
        node ans=node(0,0,0);
        vis[1]=1;
        while(!que.empty())
        {
            node now=que.top();
            que.pop();
            if(vis[now.id])continue;
            int v=now.id,u=find(pa[v]);
            if(vis[u])
            {
                ans=merge(ans,now);
                vis[v]=1;
            }
            else
            {
                fa[u]=v;
                pa[v]=pa[u];
                vis[u]=1;
                m[v]=merge(m[u],now);
                que.push(m[v]);
            }
        }
        printf("%lld\n",ans.a);
    }
    return 0;
}

Problem I. Random Sequence

HDU - 6327

题解转载自:https://blog.csdn.net/V5ZSQ/article/details/82377492

在这里插入图片描述
∑ i = 1 n − 3 \sum_{i=1}^{n-3} i=1n3改为 ∏ i = 1 n − 3 \prod_{i=1}^{n-3} i=1n3

在这里插入图片描述

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 1000000007
int mul(int x,int y)
{
    ll z=1ll*x*y;
    return z-z/mod*mod;
}
int add(int x,int y)
{
    x+=y;
    if(x>=mod)x-=mod;
    return x;
}
int Pow(int x,int y)
{
    int ans=1;
    while(y)
    {
        if(y&1)ans=(ll)ans*x%mod;
        x=(ll)x*x%mod;
        y>>=1;
    }
    return ans;
}
int T,n,m,a[105],v[105];
int res,dp[105][1500],id[105][105][105],gcd[105][105],suf[1500][105],val[1500][105];
void init(int n=100)
{
    for(int i=1;i<=n;i++)gcd[i][0]=gcd[0][i]=i;
    for(int i=1;i<=n;i++)
    {
        gcd[i][i]=i;
        for(int j=1;j<i;j++)
            gcd[i][j]=gcd[j][i]=gcd[j][i%j];
    }
    res=0;
    for(int x=1;x<=n;x++)
        for(int y=x;y<=n;y+=x)
            for(int z=y;z<=n;z+=y)
                id[x][y][z]=res++;
    for(int x=1;x<=n;x++)
        for(int y=x;y<=n;y+=x)
            for(int z=y;z<=n;z+=y)
                for(int w=1;w<=n;w++)
                {
                    suf[id[x][y][z]][w]=id[gcd[y][w]][gcd[z][w]][w];
                    val[id[x][y][z]][w]=gcd[x][w];
                }
}
int main()
{
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)scanf("%d",&v[i]);
        memset(dp,0,sizeof(dp));
        int cnt=id[m][m][m]+1;
        for(int i=(a[1]?a[1]:1);i<=(a[1]?a[1]:m);i++)
            for(int j=(a[2]?a[2]:1);j<=(a[2]?a[2]:m);j++)
                for(int k=(a[3]?a[3]:1);k<=(a[3]?a[3]:m);k++)
                    dp[3][id[gcd[i][gcd[j][k]]][gcd[j][k]][k]]++;
        for(int i=4;i<=n;i++)
            for(int j=0;j<cnt;j++)
                if(dp[i-1][j])
                    for(int k=(a[i]?a[i]:1);k<=(a[i]?a[i]:m);k++)
                        dp[i][suf[j][k]]=add(dp[i][suf[j][k]],mul(v[val[j][k]],dp[i-1][j]));
        int ans=0;
        for(int i=0;i<cnt;i++)ans=add(ans,dp[n][i]);
        int num=0;
        for(int i=1;i<=n;i++)
            if(!a[i])num++;
        ans=mul(ans,Pow(Pow(m,mod-2),num));
        printf("%d\n",ans); 
    }
    return 0;
}

Problem J. Rectangle Radar Scanner

HDU - 6328

题解转载自:https://blog.csdn.net/zstu_zy/article/details/82990741
题意:给出一个n*n的二维空间,上面有一些点,每个点有一个权值wi,给出m个矩形,问每个矩形里面的权值积,最大权值和最小权值。做法:分治,先把问题转化一下,用 (xl,xr,yl,yr)表示矩形,如果有一些矩形的xl一样,那么把矩形按照xr从小到大排序,建立一棵线段树,线段树的点保存纵坐标上面的数的积,最大值,最小值。每次xr变化的时候就把上一个xr到这个xr之间的点填到线段树里面,然后因为xr是递增的,那么对于某个点来说有效的点是从xl到xr之间的点,纵坐标在yl到yr之间,对于某个矩形来说,它的结果可以用一个query来得到,复杂度是log的。然后回到这一题,处理一个区间的时候,把越过区间中间点的矩形沿着中间点切成两半,那么处理两边的矩形就可以用上面的方法了。对两边分别用线段树跑一遍就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;

struct node{int x,l,r,id;}nl[N],nr[N];
bool cmp1(node a,node b){return a.x > b.x;}
bool cmp2(node a,node b){return a.x < b.x;}
struct node2{int xl,xr,yl,yr,id;}qs[N],ts[N];
struct node3{int sum,mn,mx;}sum[N<<2];
int y[N],w[N];
int mul[N],mx[N],mn[N];
int mod;
int n,m;
void build(int l,int r,int rt){
    sum[rt].sum= 1;
    sum[rt].mx = 0;
    sum[rt].mn = 1e9;
    if(l == r) return ;
    int mid = l+r>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}

node3 pushup(node3 a,node3 b){
    a.sum= 1LL*a.sum*b.sum%mod;
    a.mx = max(a.mx,b.mx);
    a.mn = min(a.mn,b.mn);
    return a;
}

void update(int x,int d,int l,int r,int rt){
    if(l == r){
        sum[rt].sum = sum[rt].sum*1LL*d%mod;
        sum[rt].mx = max(sum[rt].mx,d);
        sum[rt].mn = min(sum[rt].mn,d);
        return ;
    }
    int mid = l+r>>1;
    if(mid >= x) update(x,d,l,mid,rt<<1);
    else update(x,d,mid+1,r,rt<<1|1);
    //pushup(rt);
    sum[rt] = pushup(sum[rt<<1],sum[rt<<1|1]);
}

void cle(int x,int l,int r,int rt){
    sum[rt].sum= 1;
    sum[rt].mn = 1e9;
    sum[rt].mx = 0;

    if(l == r) return;
    int mid = l+r>>1;
    if(mid >= x) cle(x,l,mid,rt<<1);
    else cle(x,mid+1,r,rt<<1|1);
}
node3 query(int L,int R,int l,int r,int rt){
    if(L <= l && R >= r){
        return sum[rt];
    }
    int  mid = l+r>>1;
//    node3 ret;
//    ret.sum = 1,ret.mx = 0,ret.mn = 1e9;
//    if(mid >= L) ret = pushup(ret,query(L,R,l,mid,rt<<1));
//    if(mid < R) ret = pushup(ret,query(L,R,mid+1,r,rt<<1|1));
    if(mid >= L&& mid < R) return pushup(query(L,R,l,mid,rt<<1),query(L,R,mid+1,r,rt<<1|1));
    if(mid >= L) return query(L,R,l,mid,rt<<1);
    if(mid < R) return query(L,R,mid+1,r,rt<<1|1);
}


void solve(int l,int r,int L,int R){

    if(l > r) return ;
    int mid = L+R>>1;
    int cl = 0,cr = 0,ll = l,lr = r;
    for(int i = l;i <= r;i ++){
        if(qs[i].xr < mid){ts[ll++] = qs[i];continue;}
        if(qs[i].xl > mid){ts[lr--] = qs[i];continue; }
        nl[cl++] = (node){qs[i].xl,qs[i].yl,qs[i].yr,qs[i].id};
        nr[cr++] = (node){qs[i].xr,qs[i].yl,qs[i].yr,qs[i].id};
    }
    sort(nl,nl+cl,cmp1);
    for(int i = 0,j = mid;i < cl;i ++){
        for(;j >= L && j >= nl[i].x;j --) {update(y[j],w[j],1,n,1);}
        node3 tmp= query(nl[i].l,nl[i].r,1,n,1);
        //cout <<tmp.sum << ' '<< tmp.mx << ' '<<tmp.mn<< endl;
        mul[nl[i].id] = mul[nl[i].id]*1ll*tmp.sum%mod;
        mx[nl[i].id] = max(mx[nl[i].id],tmp.mx);
        mn[nl[i].id] = min(mn[nl[i].id],tmp.mn);
    }
    for(int i = L;i <= mid;i ++) cle(y[i],1,n,1);
    sort(nr,nr+cr,cmp2);
    for(int i = 0,j = mid+1;i < cr;i ++){
        while(j <= R && j <= nr[i].x){
            update(y[j],w[j],1,n,1);
            //cout <<"!!" <<y[j] << ' '<<w[j] << endl;
            j++;
        }
        node3 tmp = query(nr[i].l,nr[i].r,1,n,1);
        //cout <<tmp.sum << ' '<< tmp.mx << ' '<<tmp.mn<< endl;
        mul[nr[i].id] = mul[nr[i].id]*1ll*tmp.sum%mod;
        mx[nr[i].id] = max(mx[nr[i].id],tmp.mx);
        mn[nr[i].id] = min(mn[nr[i].id],tmp.mn);
    }

    for(int i = mid;i <= R;i ++) cle(y[i],1,n,1);
    for(int i = l;i <= r;i ++) qs[i] = ts[i];
    solve(l,ll-1,L,mid-1);
    solve(lr+1,r,mid+1,R);
}




int main(){
    int T;
    cin >> T;
    while(T--){
        int ba,bb,bc,bd,p,q,r,mk;
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++) scanf("%d %d",&y[i],&w[i]);
        scanf("%d",&m);
        scanf("%d %d %d %d %d %d %d %d %d",&ba,&bb,&bc,&bd,&p,&q,&r,&mod,&mk);
        for(int i = 1;i <= m;i ++) mul[i] = 1,mx[i] = 0,mn[i] = 1e9;
        for(int i = 1;i <= m;i ++){
            int a,b,c,d;
            a = (1LL*p*ba+1LL*q*bb+r)%mod;
            b = (1LL*p*bb+1LL*q*ba+r)%mod;
            c = (1LL*p*bc+1LL*q*bd+r)%mod;
            d = (1LL*p*bd+1LL*q*bc+r)%mod;
            qs[i].xl = min(a%n,b%n)+1;
            qs[i].xr = max(a%n,b%n)+1;
            qs[i].yl = min(c%n,d%n)+1;
            qs[i].yr = max(c%n,d%n)+1;
            //cout << qs[i].xl <<  ' '<<qs[i].xr << ' '<<qs[i].yl << ' ' << qs[i].yr << endl;
            qs[i].id = i;
            ba = a,bb = b,bc = c,bd = d;
        }
        mod = mk;
        build(1,n,1);
        solve(1,m,1,n);
        long long ans = 0;
        for(int i = 1;i <= m;i ++){
            //cout << i << ' '<<mul[i] << ' '<<mx[i] << ' '<<mn[i] << endl;
            if(mx[i]) ans += mul[i]^mx[i]^mn[i];
        }

        printf("%lld\n",ans);
    }

    return 0;
}


Problem K. Transport Construction

HDU - 6329

题解转载自:https://blog.csdn.net/wcy_1122/article/details/82655444
考虑一种奇怪的求mst的办法,对于每个点求出离它最近的点,将其两两连边。 n个点n条边最后肯定是一些环和一些树。 我们把连通的点合并起来,然后再在缩好点的图上对于每个点再求一下不再同一个联通快的最近边,再跑合并一下。 每次合并,假设有n个联通快,合并之后最多只会剩下n/2个联通快。 所以最多只要log次合并就可以完成mst的构建。 至于怎么找最近点,因为题目规定的距离是点积,其实就只有下凸壳上的点会成为答案。 所以我们按节点联通快的颜色分治,左边区间的点和右边的凸包合并,右边区间的点和左边的凸包合并。这样复杂度就是O(nlogn^2)。

#include<bits/stdc++.h>
#define ll long long
#define inf 999999999999999ll
#define N 100010
using namespace std;
int T,n,m,cnt,X[N],Y[N],w[N],fa[N],pos[N],h[N];
ll ans,f[N];
struct node{
  ll x,y,p;
  ll operator*(const node &h)const{return x*h.x+y*h.y;}
  bool operator<(const node &h)const{return p<h.p;}
  ll operator+(const node &h)const{return x*h.y-y*h.x;}
  node operator-(const node &h)const{return (node){x-h.x,y-h.y};}
}t[N],s[N];
vector<node>q[N*4];
bool cmp1(const node &a,const node &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
bool cmp2(const node &a,const node &b){return a.y*b.x<b.y*a.x;}

void insert(int now,const node &x)
{
  int top=q[now].size()-1;
  while(top>0&&(q[now][top]-q[now][top-1])+(x-q[now][top])<=0)q[now].pop_back(),top--;
  q[now].push_back(x);
}

int solve(int l,int r,int L,int R)
{
  int now=++cnt;
  if(L==R)
  {
    sort(t+l,t+r+1,cmp1);
    for(int i=l;i<=r;i++)insert(now,t[i]);
    sort(t+l,t+r+1,cmp2);

    return now;
  }
  int mid=L+R>>1,po;
  for(int i=l;i<=r;i++)
    if(t[i].p>mid){po=i-1;break;}
  int lc=solve(l,po,L,mid),rc=solve(po+1,r,mid+1,R);

  for(int i=l,j=0;i<=po;i++)
  {
    while(j<q[rc].size()-1&&q[rc][j]*t[i]>=q[rc][j+1]*t[i])j++;
    if(q[rc][j]*t[i]<f[t[i].p])
      f[t[i].p]=q[rc][j]*t[i],pos[t[i].p]=q[rc][j].p;
  }
  for(int i=po+1,j=0;i<=r;i++)
  {
    while(j<q[lc].size()-1&&q[lc][j]*t[i]>=q[lc][j+1]*t[i])j++;
    if(q[lc][j]*t[i]<f[t[i].p])
      f[t[i].p]=q[lc][j]*t[i],pos[t[i].p]=q[lc][j].p;
  }

  int x=0,y=0;
  for(;x<q[lc].size()&&y<q[rc].size();)
  {
    if(q[lc][x].x<q[rc][y].x)insert(now,q[lc][x++]);
    else insert(now,q[rc][y++]);
  }
  while(x<q[lc].size())insert(now,q[lc][x++]);
  while(y<q[rc].size())insert(now,q[rc][y++]);
  int tot=l;
  for(x=l,y=po+1;x<=po&&y<=r;)
  {
    if(t[x].y*t[y].x<t[y].y*t[x].x)s[tot++]=t[x++];
    else s[tot++]=t[y++];
  }
  while(x<=po)s[tot++]=t[x++];
  while(y<=r)s[tot++]=t[y++];
  for(int i=l;i<=r;i++)t[i]=s[i];
  return now;
}

int find(int x)
{
  if(fa[x]==x)return x;
  return fa[x]=find(fa[x]);
}

int main()
{
  int x,y;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d",&n);ans=0;
    for(int i=1;i<=n;i++)scanf("%d%d",&X[i],&Y[i]);
    for(int i=1;i<=n;i++)fa[i]=i,w[i]=0;
    while(1)
    {
      m=0;cnt=0;
      for(int i=1;i<=n;i++)
      {
        x=find(i);if(!w[x])h[w[x]=++m]=x;
        t[i]=(node){X[i],Y[i],w[x]};
      }
      if(m==1)break;
      for(int i=1;i<=m;i++)f[i]=inf;
      sort(t+1,t+n+1);solve(1,n,1,m);
      for(int i=1;i<=m;i++)
      {
        x=find(h[i]);y=find(h[pos[i]]);
        if(x!=y)ans+=f[i],fa[y]=x;
      }
      for(int i=1;i<=cnt;i++)q[i].clear();
      for(int i=1;i<=n;i++)w[i]=0;
    }
    printf("%lld\n",ans);
  }
  return 0;
}

Problem L. Visual Cube

HDU - 6330
模拟题,输入a b c 输出对应的立方体

#include<bits/stdc++.h>
using namespace std;

char cube[500][500]= {0};
void solve(int a,int b,int c)
{
    int chang=2*c+1+2*b;
    int kuan=2*b+2*a+1;
    /**/
    for(int i=0; i<chang; i++)
        for(int j=0; j<kuan; j++)
            cube[i][j]='.';
    /**/
    for(int i=0; i<chang; i++)
        for(int j=0; j<kuan; j++)
        {
            if(i%2==0&&j%2==0&&(i+j>=2*b)&&(i+j<chang+kuan-2*b ))
                cube[i][j]='+';
            if(i%2==0&&j%2==1)
            {
                if(i<b*2&&j>2*b-i&&j<kuan-i)
                    cube[i][j]='-';
                if(i>=b*2&&j<2*a+1)
                    cube[i][j]='-';
            }
            // '/'
            if(i%2==1&&j%2==1)
            {
                if(i+j>=2*b&&i+j<chang+kuan-2*b)
                if(i<2*b||i>2*b&&j>=2*a+1)
                    cube[i][j]='/';
            }
            // '|'
            if(i%2==1&&j%2==0)
            {
                if(i<2*b&&i+j>=kuan&&i+j<chang+kuan-2*b-1)
                    cube[i][j]='|';
                if(i>=2*b&&i+j<chang+kuan-2*b-1)
                    cube[i][j]='|';
            }
        }
}
int t;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(cube,0,sizeof(cube));
        int a=6,b=2,c=4;
        scanf("%d %d %d",&a,&b,&c);
        solve(a,b,c);
        for(int i=0; i<2*c+1+2*b; i++)
            printf("%s\n",cube[i]);
    }
    return 0;
}

Problem M. Walking Plan

HDU - 6331
题解转载自:https://blog.csdn.net/qq_31759205/article/details/81293605
题意:一个有向图,有q次询问:从s到t至少走k步需要的最短距离是多少
题解:因为n很小,可以考虑floyd。首先想到k层最短路,可是k最大值为10000,于是想到倍增的写法,然而发现一次询问最小的复杂度也是 O ( n 2 ∗ l o g k ) O(n^2*logk) O(n2logk)
考虑用分块。 m p [ k ] [ i ] [ j ] mp[k][i][j] mp[k][i][j]为从i走到j恰好走k步的最小距离,
m p [ k ] [ i ] [ j ] = m i n ( m p [ k − 1 ] [ i ] [ p ] + m p [ 1 ] [ p ] [ j ] ) mp[k][i][j] = min(mp[k-1][i][p] + mp[1][p][j]) mp[k][i][j]=min(mp[k1][i][p]+mp[1][p][j])
E [ k ] [ i ] [ j ] E[k][i][j] E[k][i][j]为从i走到j恰好走k*100步的最小距离,
E [ k ] [ i ] [ j ] = m i n ( E [ k − 1 ] [ i ] [ p ] + E [ 1 ] [ p ] [ j ] ) E[k][i][j] = min(E[k-1][i][p] + E[1][p][j]) E[k][i][j]=min(E[k1][i][p]+E[1][p][j])
每次查询的时候,枚举中间点,ans = min(E[k/100][u][x] + mp[k%100][x][v])

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=a-1;i>=b;--i)
#define fuck(x) cout<<'['<<#x<<' '<<(x)<<']'<<endl
#define FIN freopen("in.txt","r",stdin);
using namespace std;
 
typedef long long ll;
typedef pair<int, int> PII;
 
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MX = 55;
int T, n, m;
int mp[MX * 2][MX][MX], E[MX * 2][MX][MX];
int d[MX][MX], t[MX][MX];
 
void mul (int a[MX][MX], int b[MX][MX], int c[MX][MX]) {
    rep (i, 1, n + 1) rep (j, 1, n + 1) t[i][j] = INF;
    rep (i, 1, n + 1) rep (j, 1, n + 1) rep (k, 1, n + 1) t[i][j] = min (t[i][j], a[i][k] + b[k][j]);
    rep (i, 1, n + 1) rep (j, 1, n + 1) c[i][j] = min (c[i][j], t[i][j]);
}
 
int main() {
    //FIN;
    cin >> T;
    while (T--) {
        scanf ("%d%d", &n, &m);
        rep (k, 0, 101) rep (i, 1, n + 1) rep (j, 1, n + 1) mp[k][i][j] = E[k][i][j] = INF;
        rep (i, 1, n + 1) mp[0][i][i] = E[0][i][i] = 0;
        rep (i, 0, m) {
            int u, v, w;
            scanf ("%d%d%d", &u, &v, &w);
            mp[1][u][v] = min (mp[1][u][v], w);
        }
        rep (i, 1, n + 1) rep (j, 1, n + 1) d[i][j] = mp[1][i][j];
        rep (k, 1, n + 1) rep (i, 1, n + 1) rep (j, 1, n + 1) d[i][j] = min (d[i][j], d[i][k] + d[k][j]);
        rep (x, 2, 101) mul (mp[x - 1], mp[1], mp[x]);
        rep (x, 1, 101) mul (E[x - 1], mp[100], E[x]);
        //一定要加上这句,因为有可能不能刚好100*x步到达终点
        rep (x, 0, 101) mul (E[x], d, E[x]);
        per (k, 100, 0) rep (i, 1, n + 1) rep (j, 1, n + 1) mp[k][i][j] = min (mp[k][i][j], mp[k + 1][i][j]);
        int u, v, k, q;
        scanf ("%d", &q);
        while (q--) {
            scanf ("%d%d%d", &u, &v, &k);
            int a = k / 100, b = k % 100;
            int ans = INF;
            rep (x, 1, n + 1) ans = min (ans, mp[b][u][x] + E[a][x][v]);
            printf ("%d\n", ans == INF ? -1 : ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值