• 9.8-9.9多校互测与牛客网提高一测


    9.8-9.9多校互测与牛客网提高一测

    多校互测

    貌似是比较尴尬,本来100+40+30 (->) 70+30+30 。原因竟然是(dots)
    貌似是只做了(day1)至于做不做(day2)还是两说,但是先写题解为妙。

    A

    这是一道语文题,出题人毒瘤到水题不能告诉你这题目是什么意思(dots)
    然后考试的时候读了好长时间的题面,硬是没想出来。
    然后考试最后半小时终于看懂了题目什么意思,但就是不想写正解了,于是索性打个暴力,更气人的是,暴力打完还剩(25min),还是不想写正解,于是想优化就加上了离散化代码到达了(50)行。考完试才知道正解(20)行。

    于是贴一下我(70)分离散化代码

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #define LL long long
    using namespace std;
    struct edge {
    	int id,a,ans;
    } e[100001];
    int n,b[100001],cnt,vis[100001],d[100001];
    bool cmp(edge x,edge y) {
    	return x.a<y.a;
    }
    bool cml(edge x,edge y) {
    	return x.id<y.id;
    }
    void calc(int x) {
    	int p=lower_bound(b+1,b+1+cnt,e[x].a)-b;
    	int ans=0;
    	for(int i=p-1; i>=1; i--) {
    		if(e[x].a%b[i]==0)ans+=vis[i];
    		if(b[i]==0)break;
    	}
    	ans+=vis[p]-1;
    	e[x].ans=ans;
    }
    int main() {
    	freopen("A.in","r",stdin);
    	freopen("A.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) scanf("%d",&e[i].a),e[i].id=i,e[i].ans=0;
    	sort(e+1,e+1+n,cmp);
    	for(int i=1; i<=n; i++) {
    		if(e[i].a!=e[i-1].a)b[++cnt]=e[i].a;
    		vis[cnt]++;
    	}
    	for(int i=1; i<=n; i++)d[i]=lower_bound(b+1,b+1+cnt,e[i].a)-b;
    	for(int i=1; i<=n; i++)calc(i);
    	sort(e+1,e+1+n,cml);
    	for(int i=1; i<=n; i++)printf("%d
    ",e[i].ans);
    	return 0;
    }
    

    B

    就是给一段序列,然后求字母的出现次数最大的减出现次数最小的
    我一开始就想,怎么样才能存储字母呢,我想了个自以为不错的方法——树状数组(最后发现前缀和统计即可)

    //30pts
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    using namespace std;
    int vis[27],tot,tree[27][1000002],n,fin[27],answ;
    int to_num(char s){
    	return s-96;
    }
    int lowbit(int k){
    	return k&(-k);
    }
    void add(int id,int x,int y){
    	while(x<=n){
    		tree[id][x]+=y;
    		x+=lowbit(x);
    	}
    }
    int ask(int id,int x){
    	int ans=0;
    	while(x!=0){
    		ans+=tree[id][x];
    		x-=lowbit(x);
    	}
    	return ans;
    }
    void work(int l,int r){
    	int maxn=0,minn=0x7fffffff;
    	for(int i=1;i<=tot;i++){
    		int p=ask(fin[i],r)-ask(fin[i],l-1);
    		if(p==0)continue;
    		maxn=max(maxn,p);
    		minn=min(minn,p);
    	}
    	answ=max(answ,maxn-minn);
    }
    int main()
    {
    	// freopen("B.in","r",stdin);
    	// freopen("B.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		char s;
    		cin >> s;
    		int p=to_num(s);
    		if(!vis[p]){
    			vis[p]=1;
    			fin[++tot]=p;
    		}
    		add(p,i,1);
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j++)
    			work(i,j);
    	printf("%d",answ);
    	return 0;
    }
    

    意思就是枚举左右端点,然后跑树状数组即可

    但是我又通过询问李小浪,(Get)(60pts)的方法:
    用前缀和统计,然后枚举两个字符,然后再枚举每个数字在哪个区间出现的次数。也就是说用前缀和表示为((a_j-a_i)-(b_j-b_i))的值最小对吧,然后换一下形式((a_j-b_j)-(a_i-b_i)),嗯,因为(j)肯定比(i)大,所以枚举的时候就能存下来。从而可以拿到(60)分的好成绩。

    C

    题目的意思很简单啊,就是每一点的最短路的最后一条路径删去。考完试dalao们激情交流,枚举到倒数第二个点然后加上第二个点最短路啥啥的,但是仔细一想不对,因为有可能最短路就改变了。然后我暴力(n)(spfa)拿到30pts,他们只拿到了10pts。

    //30pts code
    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<cmath>
    #define LL long long
    
    using namespace std;
    
    struct edge {
    	int next,to,dis;
    } e[400001];
    int head[400001],tot,n,m,dis[100001],minn[100001],vis[100001],pre[100001];
    void add(int x,int y,int z) {
    	e[++tot].next=head[x];
    	e[tot].to=y;
    	head[x]=tot;
    	e[tot].dis=z;
    }
    
    void spfa(int s) {
    	queue<int>q;
    	q.push(s);
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f,sizeof(dis));
    	dis[s]=0;
    	vis[s]=1;
    	while(!q.empty()) {
    		int u=q.front();
    		q.pop();
    		vis[u]=0;
    		for(int i=head[u]; i; i=e[i].next) {
    			int v=e[i].to;
    			if(dis[v]>dis[u]+e[i].dis) {
    				pre[v]=u;
    				dis[v]=dis[u]+e[i].dis;
    				if(!vis[v])q.push(v),vis[v]=1;
    			}
    		}
    	}
    }
    void spfa_(int s,int x,int y) {
    	queue<int>q;
    	q.push(s);
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f,sizeof(dis));
    	dis[s]=0;
    	vis[s]=1;
    	while(!q.empty()) {
    		int u=q.front();
    		q.pop();
    		vis[u]=0;
    		for(int i=head[u]; i; i=e[i].next) {
    			int v=e[i].to;
    			if((u==x && v==y) || (v==x && u==y))continue;
    			if(dis[v]>dis[u]+e[i].dis) {
    				dis[v]=dis[u]+e[i].dis;
    				if(!vis[v])q.push(v),vis[v]=1;
    			}
    		}
    	}
    }
    int main() {
    	// freopen("C.in","r",stdin);
    	// freopen("C.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<=m; i++) {
    		int a,b,c;
    		scanf("%d%d%d",&a,&b,&c);
    		add(a,b,c);
    		add(b,a,c);
    	}
    	for(int i=1; i<=n; i++)minn[i]=0x7fffffff;
    	spfa(1);
    	for(int i=2;i<=n;i++) {
    		spfa_(1,i,pre[i]);
    		minn[i]=min(minn[i],dis[i]);
    	}
    	for(int i=2; i<=n; i++) {
    		if(minn[i]!=1061109567)
    			printf("%d
    ",minn[i]);
    		else if(minn[i]==1061109567 || minn[i]==2147483647)printf("-1
    ");
    	}
    	return 0;
    }
    

    赛后看题解,真恶心人,最小路径树是什么鬼?are you sure noip kao ?
    不学了

    牛客网比赛

    T1中位数

    怎么说呢暴力本来是要跑60的啊,n方logn可是能过的啊,偏偏就30

    //30pts
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N = 100001;
    int n,len,maxn;
    int a[N],b[N];
    int main(){
    	scanf("%d%d",&n,&len);
    	for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[i]=a[i];}
    	for(int l=1,r=l+len-1;r<=n;l++,r++){
    		sort(b+l,b+1+r);
    		if(len%2==1)maxn=max(maxn,b[l+(len+1)/2-1]);
    		else maxn=max(maxn,b[l+len/2-1]);
    		for(int i=l;i<=r;i++)b[i]=a[i];
    	}
    	printf("%d",maxn);
    }
    

    然后第二档60pts主席树查找区间第k大,弃疗弃疗,直接看正解

    正解是枚举一个可能值,然后会发现可以二分,然后在记录。
    对于第三档数据,直接枚举最终可能的答案x。把数字(>)x的记为1, (le)x的记为-1。检验是否存在和$ge(0的区间。可以维护一个前缀和的前缀最大值。时间复杂度)O(Mn)$,
    M为不同的数的个数。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 100005;
    
    int a[maxn], n, L;
    
    bool test(int z)
    {
    	static int b[maxn];
    	for(int i = 1;i <= n;i ++)
    		if (a[i] >= z) b[i] = 1; else b[i] = -1;
    	for(int i = 1, mi = (1 << 30);i <= n;i ++)
    	{
    		if (i >= L) mi = min(mi, b[i - L]);
    		b[i] += b[i - 1];
    		if (i >= L && b[i] - mi > 0) return 1;
    	}
    	return 0;
    }
    
    int main()
    {
    	freopen("median.in","r",stdin),freopen("median.out","w",stdout);
    	scanf("%d%d", &n, &L);
    	for(int i = 1;i <= n;i ++) scanf("%d", &a[i]);
    	int l = 0, r = int(1e9), tmp;
    	for(int mid;l <= r;)
    	{
    		mid = l + r >> 1;
    		if (test(mid)) tmp = mid, l = mid + 1; else r = mid - 1;
    	}
    	printf("%d
    ", tmp);
    	return 0;
    }
    

    数数字

    唯一一道暴力分多的题目。。
    30pts不用说了,直接上代码了

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define LL long long
    using namespace std;
    LL l,r,l1,r1;
    int f[50000001];
    int ans;
    int fw(int x) {
    	int z=1;
    	while(x) {
    		if(x<50000001)
    			if(f[x])return z*f[x];
    		z=z*(x%10);
    		x/=10;
    	}
    	if(x<50000001)
    		f[x]=z;
    	return z;
    }
    int main() {
    	// freopen("1.in","r",stdin);
    	f[1]=1;
    	f[2]=2;
    	f[3]=3;
    	f[4]=4;
    	f[5]=5;
    	f[6]=6;
    	f[7]=7;
    	f[8]=8;
    	f[9]=9;
    	f[10]=0;
    	for(int i=11; i<=1000000; i++)
    		f[i]=f[i/10]*(i%10);
    	scanf("%lld%lld%lld%lld",&l,&r,&l1,&r1);
    	for(int i=l; i<=r; i++)
    		if(fw(i)>=l1&&fw(i)<=r1)ans++;
    	printf("%d",ans);
    }
    

    就是恶心的打表记忆化加暴力。但还是40pts。
    然后,正解就是数位(dp)
    先贴一下std压一下

    //code - std
    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    int co[10][10];
    ll l,r,l1,r1, pw[10][100];
    
    inline int merge(int i, int cr, int b)
    {
    	return (cr > b ? 2 : (cr == b ? i : 0));
    }
    
    ll calc_no_zero(ll x)
    {
    	if (x <= 0) return 0;
    	static ll f[2][3][60][38][27][22];
    	memset(f,0,sizeof f);
    	int cr = 0;
    	f[0][1][0][0][0][0] = 1;
    	ll ans = 0, val;
    	for(;x;x /= 10, cr ^= 1)
    	{
    		int bit = x % 10;
    		memset(f[cr ^ 1], 0, sizeof f[cr]);
    		for(int i = 0;i < 3;i ++)
    			for(int n_2 = 0;n_2 < 60;n_2 ++)
    				for(int n_3 = 0;n_3 < 38;n_3 ++)
    					for(int n_5 = 0;n_5 < 27;n_5 ++)
    						for(int n_7 = 0;n_7 < 22;n_7 ++)
    							if (val = f[cr][i][n_2][n_3][n_5][n_7])
    							{
    								ll q1 = r1 / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
    								ll p1 = (l1 - 1) / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
    								if (q1 >= 1 && p1 <= 0) ans += val;
    								if (q1 <= 0) continue;
    								for(int p = 1;p < 10;p ++)
    								{
    									int ni = merge(i, p, bit);
    									int c_2 = n_2 + co[p][2];
    									int c_3 = n_3 + co[p][3];
    									int c_5 = n_5 + co[p][5];
    									int c_7 = n_7 + co[p][7];
    									if (c_2 >= 60 || c_3 >= 38 || c_5 >= 27 || c_7 >= 22) continue;
    									f[cr ^ 1][ni][c_2][c_3][c_5][c_7] += val;
    								}
    							}
    	}
    	for(int i = 0;i < 2;i ++)
    		for(int n_2 = 0;n_2 < 60;n_2 ++)
    				for(int n_3 = 0;n_3 < 38;n_3 ++)
    					for(int n_5 = 0;n_5 < 27;n_5 ++)
    						for(int n_7 = 0;n_7 < 22;n_7 ++)
    							if (val = f[cr][i][n_2][n_3][n_5][n_7])
    							{
    								ll q1 = r1 / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
    								ll p1 = (l1 - 1) / pw[2][n_2] / pw[3][n_3] / pw[5][n_5] / pw[7][n_7];
    								if (q1 >= 1 && p1 <= 0) ans += val;
    							}
    	return ans;
    }
    
    ll calc_with_zero(ll x)
    {
    	if (l1) return 0;
    	static ll g[2][3][2][2];
    	memset(g,0,sizeof g);
    	int cr = 0;
    	ll ans = 0;
    	g[0][1][0][0] = 1;
    	for(;x;x /= 10, cr ^= 1)
    	{
    		int bit = x % 10;
    		memset(g[cr ^ 1], 0, sizeof g[cr]);
    		for(int i = 0;i < 3;i ++)
    			for(int j = 0;j < 2;j ++)
    				for(int hd = 0;hd < 2;hd ++)
    					if (g[cr][i][j][hd])
    					{
    						if (hd && j) ans += g[cr][i][j][hd];
    						for(int p = 0;p < 10;p ++)
    						{
    							int ni = merge(i, p, bit);
    							int nj = (j | (p == 0));
    							int hdt = (p > 0);
    							g[cr ^ 1][ni][nj][hdt] += g[cr][i][j][hd];
    						}
    					}
    	}
    	return ans + g[cr][0][1][1] + g[cr][1][1][1];
    }
    
    ll calc(long long x)
    {
    	return calc_no_zero(x) + calc_with_zero(x);
    }
    
    int main()
    {
    	freopen("count.in","r",stdin),freopen("count.out","w",stdout);
    	for(int i = 1;i < 10;i ++)
    	{
    		for(int j = 2;j < 10;j ++)
    			for(int k = i;k % j == 0;k /= j) co[i][j] ++;
    		pw[i][0] = 1;
    		for(int j = 1;j <= 60;j ++) pw[i][j] = pw[i][j - 1] * i;
    	}
    	cin >> l >> r >> l1 >> r1;
    	ll ans = 0;
    	if (!l) ans += (l1 == 0), ++ l;
    	if (l <= r) ans += calc(r) - calc(l - 1);
    	cout << ans << endl;
    	return 0;
    }
    

    正解可以使用记忆化搜索或者DP。这里介绍DP的做法。
    由于数位乘积只是由0到9,可以把L1,R1分类讨论,假如区间包含0,则原来的数字分为包含至少一个零,和完全不包含0两类。前一类可以使用简单的数位DP计算。接下来介绍后一类。
    由于只包含1到9,所以只用记录乘积的质因数分解中,出现了多少2, 3, 5, 7。题目中的L1,R1不超过10^18,可以计算出2最多为59个, 3最多为37, 5最多为26, 7最多为21。设F[i][c_2][c_3][c_5][c_7]表示考虑到第i位,乘积状态为c_2,c_3,c_5,c_7的数位DP情况。接着就是经典数位DP做法。空间可能比较紧,需要用滚动数组

    保护

    考试想了一个树剖的方法,但是临时改题面了,(...)好端端的100跑了。改完题面什么破题, are you sure noip tigaozu?

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 200005;
    
    struct node
    {
    	int l,r,cnt;
    }T[maxn * 100];
    
    vector<int> lk[maxn];
    int fa[maxn][20],dep[maxn],ord[maxn],cnt,n,m,ans[maxn];
    
    void dfs(int now,int pre)
    {
    	dep[now] = dep[pre] + 1;
    	fa[now][0] = pre;
    	for(int i = 1;(1 << i) <= dep[now];i ++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
    	for(int i = 0;i < lk[now].size();i ++)
    		if (lk[now][i] != pre)
    			dfs(lk[now][i], now);	
    }
    
    void insert(int l,int r,int p,int &jd)
    {
    	if (!jd) jd = ++ cnt;
    	T[jd].cnt ++;
    	if (l == r) return;
    	int mid = l + r >> 1;
    	if (p <= mid) insert(l,mid,p,T[jd].l); else insert(mid + 1,r,p, T[jd].r);
    }
    
    int merge(int l,int r,int a,int b)
    {
    	if ((!a) || (!b)) return a + b;
    	int jd = ++ cnt;
    	T[jd].cnt = T[a].cnt + T[b].cnt;
    	if (l == r) return jd;
    	int mid = l + r >> 1;
    	T[jd].l = merge(l,mid,T[a].l,T[b].l);
    	T[jd].r = merge(mid + 1,r,T[a].r, T[b].r);
    	return jd;
    }
    
    int find_k(int l,int r,int k,int jd)
    {
    	if (T[jd].cnt < k) return (1 << 30);
    	if (l == r) return l;
    	int mid = l + r >> 1;
    	if (T[T[jd].l].cnt >= k) return find_k(l,mid,k,T[jd].l);
    	return find_k(mid + 1,r,k - T[T[jd].l].cnt, T[jd].r);	
    }
    
    void dfs_w(int now,int pre)
    {
    	for(int i = 0;i < lk[now].size();i ++)
    		if (lk[now][i] != pre) 
    		{
    			dfs_w(lk[now][i], now);
    			ord[now] = merge(1,n,ord[now], ord[lk[now][i]]);
    		}
    }
    
    int get_lca(int u,int v)
    {
    	if (dep[u] > dep[v]) swap(u,v);
    	for(int i = 18;i + 1;i --)
    		if (dep[fa[v][i]] >= dep[u]) v = fa[v][i];
    	if (u == v) return u;
    	for(int i = 18;i + 1;i --)
    		if (fa[v][i] != fa[u][i]) v = fa[v][i], u = fa[u][i];
    	return fa[u][0];
    }
    
    int main()
    {
    	freopen("guard.in","r",stdin),freopen("guard.out","w",stdout);
    	scanf("%d%d", &n, &m);
    	for(int i = 1,u,v;i < n;i ++)
    	{
    		scanf("%d%d", &u, &v);
    		lk[u].push_back(v), lk[v].push_back(u);
    	}
    	dfs(1,0);
    	for(int i = 1,u,v;i <= m;i ++)
    	{
    		scanf("%d%d", &u, &v);
    		int p = get_lca(u,v);
    		insert(1,n,dep[p],ord[u]),insert(1,n,dep[p],ord[v]);
    	}
    	dfs_w(1,0);
    	int q;
    	scanf("%d", &q);
    	for(int i = 1;i <= q;i ++)
    	{
    		int u,k;
    		scanf("%d%d", &u, &k);
    		printf("%d
    ", max(0, dep[u] - find_k(1,n,k,ord[u])));
    	}
    	return 0;
    }
    

    题解也看不懂,挂惨。

  • 相关阅读:
    使用Fiddler抓包(手机端app)
    IE浏览器跳转url正常,谷歌浏览器跳转报403状态
    书签
    工作记录
    MySQL索引、锁和优化
    Pytorch-张量的创建与使用方法
    网络请求
    数据挖掘的五大流程
    uni开启下拉刷新
    uni-app中的tabBar配置
  • 原文地址:https://www.cnblogs.com/ifmyt/p/9615500.html
Copyright © 2020-2023  润新知