【2019HDU暑期多校第一场泛做】

题目是按通过量做的,所以题号可能不太对。

1.Vacation

这种问题一定不能思考的太复杂,不要把自己绕进去。
我们就简单的将其分成两类:
(1) 前面没车堵我。
(2) 前面全部都堵我。

对于第一种情况,没车堵我,那么显然就是s[0]/v[0]。
重点关注第二种情况,前面所有的车都堵我。
思考了五秒钟以后我们发现,前面所有的车都堵我是啥意思,就是第一辆车最慢呗。
那么答案是什么呢?就是(s[n]+sum[n])/v[n]。因为n是第一辆车。
这样我们就可以观察出来,我的速度是受别人的限制的。
所以我们枚举每一辆前面的车,看看它会不会堵我就行了。

复杂度O(n)

#include 
using namespace std;
const int maxn = 1e5+7;
double l[maxn],s[maxn],v[maxn];
double sum[maxn];
int n;
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i=0;i<=n;i++) scanf("%lf",l+i);
		for(int i=0;i<=n;i++) scanf("%lf",s+i);
		for(int i=0;i<=n;i++) scanf("%lf",v+i);
		double ans = s[0]/v[0];
		sum[0] = 0;
		for(int i=1;i<=n;i++)
		{
			sum[i] = sum[i-1] + l[i];
			ans = max(ans,(s[i]+sum[i])/v[i]);
		}
		printf("%.6lf\n",ans);
	}
	return 0;
}

2.Path
很明显先求一个最短路,然后在最短路的边上求最小割就完事了。

#include 
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e4+7;
struct node
{
	int to;
	int cost;
};
vector<node> V[maxn];
int n,m;
int x[maxn],y[maxn],z[maxn];
ll dis[maxn];
struct edge
{
	int to;
	ll cap;
	int rev;
};
vector<edge> G[maxn];
int depth[maxn],arc[maxn];
void add_edge(int from,int to,ll cost)
{
	G[from].push_back((edge){to,cost,G[to].size()});
	G[to].push_back((edge){from,0,G[from].size()-1});
}
void bfs(int s)
{
	queue<int> q;
	memset(depth,-1,sizeof(depth));
	depth[s] = 0;
	q.push(s);
	while(!q.empty())
	{
		int v = q.front();
		q.pop();
		for(int i=0;i<G[v].size();i++)
		{
			edge &e = G[v][i];
			if(e.cap>0 && depth[e.to]<0)
			{
				depth[e.to] = depth[v]+1;
				q.push(e.to);
			}
		}
	}
}
ll dfs(int v,int t,ll f)
{
	if(v==t) return f;
	for(int &i=arc[v];i<G[v].size();i++)
	{
		edge &e = G[v][i];
		if(e.cap>0 && depth[e.to]==depth[v]+1)
		{
			ll d = dfs(e.to,t,min(f,e.cap));
			if(d>0)
			{
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}
ll dinic(int s,int t)
{
	ll flow = 0;
	while(true)
	{
		bfs(s);
		if(depth[t]<0) return flow;
		memset(arc,0,sizeof(arc));
		ll f;
		while((f=dfs(s,t,INF))>0)
		{
			flow += f;
		}
	}
}
void dijkstra(int s)
{
	struct edge
	{
		int now;
		int cost;
		bool operator<(const edge &rhs)const
		{
			return cost>rhs.cost;
		}
	};
	memset(dis,INF,sizeof(dis));
	dis[s] = 0;
	priority_queue<edge> q;
	q.push({s,dis[s]});
	while(!q.empty())
	{
		edge tmp = q.top();
		q.pop();
		for(int i=0;i<V[tmp.now].size();i++)
		{
			int v = V[tmp.now][i].to;
			if(dis[v]>dis[tmp.now]+V[tmp.now][i].cost)
			{
				dis[v] = dis[tmp.now] + V[tmp.now][i].cost;
				q.push({v,dis[v]});
			}
		}
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(G,0,sizeof(G));
		memset(V,0,sizeof(V));
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d",x+i,y+i,z+i);
			V[x[i]].push_back({y[i],z[i]});
		}
		dijkstra(1);
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<V[i].size();j++)
			{
				int v = V[i][j].to;
				if(dis[v]==dis[i]+V[i][j].cost) add_edge(i,v,V[i][j].cost);
			}
		}
		printf("%d\n",dinic(1,n));
	}
	return 0;
}

3.Operation

分析:
构造线性基,在区间[l,r]查询最大值,我们需要尽量将位置往左边放,询问的时候判断一下位置是否在L右边就行了。
具体还是看代码吧:

#include 
using namespace std;
const int maxn = 5e5+7;
int n,m;
int a[maxn],p[maxn][32],pos[maxn][32];
void insert(int x,int id)
{
	int tmp = id;
	for(int i=31;i>=0;i--)
	{
		p[id][i] = p[id-1][i];
		pos[id][i] = pos[id-1][i];
	}
	for(int i=31;i>=0;i--)
	{
		if(!(x>>i)) continue;
		if(!p[id][i])
		{
			p[id][i] = x;
			pos[id][i] = tmp;
			break;
		}
		else 
		{
			if(pos[id][i]<tmp)
			{
				swap(pos[id][i],tmp);
				swap(p[id][i],x);
			}
			x ^= p[id][i]; 
		}
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(p,0,sizeof(p));
		memset(pos,0,sizeof(pos));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",a+i);
			insert(a[i],i);
		}
		int lans = 0;
		for(int i=0;i<m;i++)
		{
			int op,x,y;
			scanf("%d",&op);
			if(op==0)
			{
				scanf("%d%d",&x,&y);
				x = (x^lans)%n+1;
				y = (y^lans)%n+1;
				if(x>y) swap(x,y);
				int ans = 0;
				for(int i=31;i>=0;i--)
				{
					if(ans<(ans^p[y][i]) && pos[y][i]>=x) ans = ans^p[y][i];
				}
				printf("%d\n",ans);
				lans = ans;
			}
			else
			{
				scanf("%d",&x);
				x ^= lans;
				insert(x,++n);
			}
		}
	}
	return 0;
}

4.String
序列自动机预处理每一个字母后缀出现的次数,预处理每个字母的后一个相同的字母出现的位置。
然后就判断合法即可。
复杂度:O(n2626)

#include 
using namespace std;
const int maxn = 3e5+7;
int k;
char s[maxn],ans[maxn];
int cnt[maxn][30],nxt[maxn][30],num[maxn],x[maxn],y[maxn];
bool check(int pos,int nlen)
{
	int len = 0,ned = 0;
	for(int i=0;i<26;i++)
	{
		if(num[i]+cnt[pos][i]<x[i]) return false;
		len += num[i] + min(y[i]-num[i],cnt[pos][i]);
		ned += max(0,x[i]-num[i]);
	}
	if(len<k) return false;
	if(ned>k-nlen) return false;
	return true;
}
int main()
{
	while(~scanf("%s%d",s+1,&k))
	{
		for(int i=0;i<26;i++) scanf("%d%d",x+i,y+i);
		int len = strlen(s+1);
		memset(cnt,0,sizeof(cnt));
		memset(nxt,-1,sizeof(nxt));
		memset(num,0,sizeof(num));
		for(int i=len-1;i>=0;i--)
		{
			for(int j=0;j<26;j++)
			{
				nxt[i][j] = nxt[i+1][j];
				cnt[i][j] = cnt[i+1][j];
			}
			nxt[i][s[i+1]-'a'] = i+1;
			cnt[i][s[i+1]-'a']++;
		}
		int pos = 0,tot = 0;
		bool isok = true;
		while(pos<=len && tot<k)
		{
			bool found = false;
			for(int i=0;i<26;i++)
			{
				if(nxt[pos][i]!=-1 && num[i]<y[i])
				{
					num[i]++;
					if(check(nxt[pos][i],tot+1))
					{
						found = true;
						ans[tot++] = i+'a';
						pos = nxt[pos][i];
						break;
					}
					num[i]--;
				}
			}
			if(!found)
			{
				isok = false;
				break;
			}
		}
		ans[tot] = '\0';
		if(isok) printf("%s\n",ans);
		else puts("-1");
	}
	return 0;
}

5.Blank
比较经典的dp
先算完答案再将不合法的答案删去的策略。
设dp[t][i][j][k]表示前t个,{0,1,2,3}出现的最后位置 (t,i,j,k不对应0,1,2,3)
且 t>i>j>k
那么就有以下四个转移:
t选t-1这个位置的数:dp[t][i][j][k]
t选i这个位置的数:dp[t][t-1][j][k]
t选j这个位置的数:dp[t][t-1][i][k]
t选k这个位置的数:dp[t][t-1][i][j]

然后枚举右端点,计数i,j,k中>l的个数,不等于x的dp置为0,即去掉了不合法的情况。

#include 
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector
#define vl vector
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 110;
const int mod = 998244353;
ll dp[2][maxn][maxn][maxn];
int n,m;
struct node
{
	int l,x;
};
vector<node> v[maxn];

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(v,0,sizeof(v));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			v[r].push_back({l,x});
		}
		int c = 0;
		dp[c][0][0][0] = 1;
		for(int t=1;t<=n;t++)
		{
			c ^= 1;
			for(int i=0;i<=t;i++)
			{
				for(int j=0;j<=i;j++)
				{
					for(int k=0;k<=j;k++)
					{
						dp[c][i][j][k] = 0;
					}
				}
			}
			for(int i=0;i<t;i++)
			{
				for(int j=0;j<=i;j++)
				{
					for(int k=0;k<=j;k++)
					{
						dp[c][i][j][k] = (dp[c][i][j][k]+dp[c^1][i][j][k])%mod;
						dp[c][t-1][j][k] = (dp[c][t-1][j][k]+dp[c^1][i][j][k])%mod;
						dp[c][t-1][i][k] = (dp[c][t-1][i][k]+dp[c^1][i][j][k])%mod;
						dp[c][t-1][i][j] = (dp[c][t-1][i][j]+dp[c^1][i][j][k])%mod;
					}
				}
			}
			for(int p=0;p<v[t].size();p++)
			{
				int l = v[t][p].l,x = v[t][p].x;
				for(int i=0;i<=t;i++)
				{
					for(int j=0;j<=i;j++)
					{
						for(int k=0;k<=j;k++)
						{
							int cnt = 1;
							if(i>=l) cnt++;
							if(j>=l) cnt++;
							if(k>=l) cnt++;
							if(cnt!=x) dp[c][i][j][k] = 0;
						}
					}
				}
			}
		}
		ll ans = 0;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<=i;j++)
			{
				for(int k=0;k<=j;k++)
				{
					ans = (ans+dp[c][i][j][k])%mod;
				}
			 } 
		}
		printf("%lld\n",ans);
	}	
	return 0;
}

6.Code
给两类点,能否找到一条直线将其划分为两部分。
判断凸包交的裸题。
凸包交时当且仅当:
1.凸包上的任意点都在另一个凸包的外面
2.凸包的任意线段都与另一个凸包不相交

#include 
using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);
class Point {
public:
    double x, y;
    Point(double x = 0, double y = 0) : x(x), y(y) {}
    Point operator+(Point a) {
        return Point(a.x + x, a.y + y);
    }
    Point operator-(Point a) {
        return Point(x - a.x, y - a.y);
    }
    bool operator<(const Point &a) const {
        if (x == a.x)
            return y < a.y;
        return x < a.x;
    }
    bool operator==(const Point &a) const {
        if (fabs(x - a.x) < eps && fabs(y - a.y) < eps)
            return 1;
        return 0;
    }
    double length() {
        return sqrt(x * x + y * y);
    }
};

typedef Point Vector;

double cross(Vector a, Vector b) {
    return a.x * b.y - a.y * b.x;
}

double dot(Vector a, Vector b) {
    return a.x * b.x + a.y * b.y;
}

bool isclock(Point p0, Point p1, Point p2) {
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if (cross(a, b) < -eps)
        return true;
    return false;
}

double getDistance(Point a, Point b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

typedef vector<Point> Polygon;
Polygon Andrew(Polygon s) {
    Polygon u, l;
    if(s.size() < 3) return s;
    sort(s.begin(), s.end());
    u.push_back(s[0]);
    u.push_back(s[1]);
    l.push_back(s[s.size() - 1]);
    l.push_back(s[s.size() - 2]);
    for(int i = 2 ; i < s.size() ; ++i) {
        for(int n = u.size() ; n >= 2 && !isclock(u[n - 2], u[n - 1], s[i]); --n) {
            u.pop_back();
        }
        u.push_back(s[i]);
    }
    for(int i = s.size() - 3 ; i >= 0 ; --i) {
        for(int n = l.size() ; n >=2 && !isclock(l[n-2],l[n-1],s[i]); --n) {
            l.pop_back();
        }
        l.push_back(s[i]);
    }
    for(int i = 1 ; i < u.size() - 1 ; i++) l.push_back(u[i]);
    return l;
}

int dcmp(double x)  {
    if (fabs(x) <= eps)
        return 0;
    return x > 0 ? 1 : -1;
}

// 判断点在线段上
bool OnSegment(Point p, Point a1, Point a2) {
    return dcmp(cross(a1 - p, a2 - p)) == 0 && dcmp(dot(a1 - p, a2 - p)) < 0;
}

// 判断线段相交
bool Intersection(Point a1, Point a2, Point b1, Point b2) {
    double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1),
            c3 = cross(b2 - b1, a1 - b1), c4 = cross(b2 - b1, a2 - b1);
    return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

// 判断点在凸包内
int isPointInPolygon(Point p, vector<Point> s) {
    int wn = 0, cc = s.size();
    for (int i = 0; i < cc; i++) {
        Point p1 = s[i];
        Point p2 = s[(i + 1) % cc];
        if (p1 == p || p2 == p || OnSegment(p, p1, p2)) return -1;
        int k = dcmp(cross(p2 - p1, p - p1));
        int d1 = dcmp(p1.y - p.y);
        int d2 = dcmp(p2.y - p.y);
        if (k > 0 && d1 <= 0 && d2 > 0) wn++;
        if (k < 0 && d2 <= 0 && d1 > 0) wn--;
    }
    if (wn != 0) return 1;
    return 0;
}

void solve(Polygon s1, Polygon s2) {
    int c1 = s1.size(), c2 = s2.size();
    for(int i = 0; i < c1; ++i) {
        if(isPointInPolygon(s1[i], s2)) {
            printf("Infinite loop!\n");
            return;
        }
    }
    for(int i = 0; i < c2; ++i) {
        if(isPointInPolygon(s2[i], s1)) {
            printf("Infinite loop!\n");
            return;
        }
    }
    for (int i = 0; i < c1; i++) {
        for (int j = 0; j < c2; j++) {
            if (Intersection(s1[i], s1[(i + 1) % c1], s2[j], s2[(j + 1) % c2])) {
                printf("Infinite loop!\n");
                return;
            }
        }
    }
    printf("Successful!\n");
}

int main() 
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
        Polygon s1, s2;
        for(int i=0;i<n;i++)
        {
        	double x,y;
        	int op;
        	scanf("%lf%lf%d",&x,&y,&op);
        	if(op==1) s1.push_back(Point(x,y));
        	else s2.push_back(Point(x,y));
		}
		if(s1.size()) s1 = Andrew(s1);
		if(s2.size()) s2 = Andrew(s2);
		solve(s1,s2);
	}
    return 0;
}

7.Typewriter
设dp[i]表示打印到第i位的最小花费
转移也很简单: dp[i] = min(dp[i-1]+p,dp[j]+q)
将字母一个一个放进后缀自动机里面,判断前缀里面是否包含后缀。
值得注意的是,对于每一个pos,(len[fa[pos]]+1 ~ len[pos]) 即是该子串的所有长度。
在判断边的时候需要结合长度判断。
引用Jiaaaaaaaqi大佬的解释方式:
/*
如何维护 pos 在符合条件的范围内,我们知道 pos 节点包含了同样性质的长度从 len[fa[pos]]+1 到 len[pos] 内的子串,我们只要保证这里面最短的子串 len[fa[pos]]+1 不要太长,需要可以表示出后面那部分的串。在插入 s[r] 之前,此时只到 r−1,需要保证 len[fa[pos]]+1≤(r−1)−(l+1)+1,插入 s[r] 后,此时到了 r,需要保证 len[fa[pos]]+1≤r−(l+1)+1
*/

#include 
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector
#define vl vector
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 2e5+7;
char s[maxn];
ll dp[maxn];
struct node
{
	int ch[26];
	int len,fa;
}point[maxn<<1];
int las = 1,tot = 1;
int siz[maxn<<1];
void add(char c)
{
	int p = las;
	int np = las = ++tot;
	siz[tot] = 1;
	point[np].len = point[p].len+1;
	for(;p && !point[p].ch[c];p=point[p].fa) point[p].ch[c] = np;
	if(!p) point[np].fa = 1;
	else
	{
		int q = point[p].ch[c];
		if(point[q].len==point[p].len+1) point[np].fa = q;
		else
		{
			int nq = ++tot;
			point[nq] = point[q];
			point[nq].len = point[p].len+1;
			point[q].fa = point[np].fa = nq;
			for(;p && point[p].ch[c]==q;p=point[p].fa) point[p].ch[c] = nq;
		}
	}
}
void init()
{
	memset(point,0,sizeof(point));
	memset(siz,0,sizeof(siz));
	las = 1,tot = 1;
	memset(dp,0,sizeof(dp));
}
int main()
{
	while(~scanf("%s",s+1))
	{
		init();
		ll w1,w2;
		scanf("%lld%lld",&w1,&w2);
		int n = strlen(s+1);
		int p = 1,l = 1;
		for(int i = 1;i<=n;i++)
		{
			dp[i] = dp[i-1]+w1;
			int c = s[i]-'a';
			while((!point[p].ch[c] || i-l+1>l-1) && l<=i)
			{
				add(s[l++]-'a');
				while(p && point[point[p].fa].len>=i-l) p = point[p].fa;
				if(!p) p = 1;
			}
			p = point[p].ch[c];
			while(p && point[point[p].fa].len>=i-l+1) p = point[p].fa;
			if(!p) p = 1;
			if(l<=i) dp[i] = min(dp[i],dp[l-1]+w2);
		}
		printf("%lld\n",dp[n]);
	}
	return 0;
}

8.Sequence
以下分析摘自题解:
【2019HDU暑期多校第一场泛做】_第1张图片

#include 
using namespace std;
typedef long long ll;
const int maxn = 2e6+7;
const int mod = 998244353;
const int G = 3;
int n,m,L,R[maxn];
int A[maxn],B[maxn];
ll fac[maxn],inv[maxn];
int cnt[10];
ll qpow(ll a,ll b)
{
    ll ans = 1;
    while(b>0)
    {
    	if(b&1) ans = ans*a%mod;
    	b>>=1;
    	a = a*a%mod;
	}
	return ans%mod;
}
void init()
{
	fac[0] = fac[1] = 1;
	for(int i=2;i<maxn;i++) fac[i] = 1LL*fac[i-1]*i%mod;
	inv[maxn-1] = qpow(fac[maxn-1],mod-2);
	for(int i=maxn-2;i>=0;i--) inv[i] = 1LL*inv[i+1]*(i+1)%mod;
}
ll C(int n,int m)
{
	if(m>n) return 0;
	return 1LL*fac[n]*inv[m]%mod*inv[n-m]%mod; 
}
void NTT(int *a,int f)
{
    for(int i = 0; i < n; i++) 
	{
		if(i < R[i]) swap(a[i],a[R[i]]);
	}
    for(int i = 1; i < n; i <<= 1)
	{
        ll gn = qpow(G,(mod - 1) / (i << 1));
        for(int j = 0; j < n; j += (i << 1))
		{
            ll g = 1;
            for(int k = 0; k < i; k++, g = g * gn % mod)
			{
                int x = a[j + k], y = g * a[j + k + i] % mod;
                a[j + k] = (x + y) % mod; 
				a[j + k + i] = (x - y + mod) % mod;
            }
        }
    }
    if(f==1) return;
    int inv = qpow(n,mod - 2); 
	reverse(a + 1,a + n);
    for(int i = 0; i < n; i++) a[i] = 1ll * a[i] * inv % mod;
}
ll solve(int *A,int *B)
{
	L = 0;
	ll ans = 0;
	int tmp = n,m = n;
	m = n + m; 
	for(n = 1; n <= m; n <<= 1) L++;
    for(int i = 0; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
    for(int i=1;i<=3;i++)
    {
    	if(!cnt[i]) continue;
    	for(int j=0;j*i<=tmp;j++) B[i*j] = C(cnt[i]+j-1,j);
    	NTT(A,1); 
		NTT(B,1);
	    for(int i = 0; i < n; i++) A[i] = 1ll * A[i] * B[i] % mod;
	    NTT(A,-1);
	    NTT(B,-1);
	    for(int i=0;i<=n+1;i++) B[i] = 0;
	    for(int i=tmp+1;i<=n+1;i++) A[i] = 0;
	}
	for(int i=0;i<=tmp;i++) ans ^= (1LL*(i+1)*A[i]);
	return ans;
}
int main()
{
	init();
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(cnt,0,sizeof(cnt));
		scanf("%d%d",&n,&m);
		n--;
		for(int i=0;i<=n;i++) scanf("%d",A+i);
	    for(int i=1;i<=m;i++) 
	    {
	    	int tmp;
	    	scanf("%d",&tmp);
	    	cnt[tmp]++;
		}
		printf("%lld\n",solve(A,B));
	}
    return 0;
}

你可能感兴趣的:(2019HDU暑期多校)