2021CCPC网络选拔赛(重赛)补题

1005

题意:输入n,m;接下来n个数,让我们从第一个数出发,一直循环走下去,每次加上这个位置的数。然后是m个询问,每次输入一个整数,问是否能走出这个数字,如果可以输出最小步数,否则输出-1。

显然我们第一步要去求n个数的前缀和以及走完一圈的增值res(最后一个前缀和)
ps:如果询问是0,直接输出0即可(要特判)。
显然如果res==0的话很明显直接记每个前缀和的位置即可。

然后再看res不为0的情况:
我们发现如果能出现这个数字 s s s的话, ∃ i \exists i i 满足 s u m [ i ] + K ∗ r e s = s sum[i]+K*res=s sum[i]+Kres=s而且 K ≥ 0 K \geq 0 K0,所以 s ≡ s u m [ i ] ( m o d    r e s ) s \equiv sum[i](\mod res) ssum[i](modres)

那我们就预处理每个 s u m [ i ] sum[i] sum[i] r e s res res的值 x x x。如果这个模数没出现过就新开一个vector,但是数据太大,要用map转换一下,虽然模数很大,但是n很小,所以vector最多也就开1e5个,可以开下。
然后对每个vector排序(这里比赛的时候傻逼了,只排了值升序,但是其实位置大的应该比位置小的更差,不懂这个为啥没看出来
最后每个询问x,就算出相应的模数s和除数y,在相应模数的vector里,找到刚好 ≤ y \leq y y的一个值 z z z,如果没有也是-1,不然答案就是 n ∗ ( y − z ) + i d x n*(y-z)+idx n(yz)+idx

一个注意点(比赛的时候也没看出来哭哭 ):就是负数算除法,因为在取模意义下, [ − n , − 1 ] [-n,-1] [n,1]这部分除数应该是 − 1 -1 1,但是直接算除法的话除了(-n)都是0,这里也要修改。

另外上面是对于 r e s > 0 res > 0 res>0的情况,如果 r e s < 0 res<0 res<0,就把每个 s u m [ i ] sum[i] sum[i]取反,每个询问也取反,就是 r e s > 0 res>0 res>0的情况了

#include<bits/stdc++.h>
#define pll pair<long long,long long>
#define line pair<double,double>
#define mp make_pair
#define pb push_back
#define ll long long
#define INF 123456789123456789
#define inf 0x3f3f3f3f
#define pi acos(-1)
#define db(x) cout<<x<<" "
#define dl(x) cout<<x<<endl
#define ls now<<1
#define rs now<<1|1
using namespace std;
const int mod = 998244353;
const int MAXN = 2e5 + 10;
const int N = 1e5 + 10;
const double eps = 1e-6;
int n, m;
map<ll, ll>ma;
struct node {
	ll val, idx;
};
vector<node>q[N];
bool cmp(node a, node b)
{
	if (a.val == b.val)return a.idx > b.idx;
	return a.val < b.val;
}
ll tot;
ll s[N];
bool check(int res, ll z, ll x)
{
	if (q[z][res].val <= x)return true;
	return false;
}
ll query(ll x, ll z)
{
	int l = 0, r = q[z].size() - 1;
	int d = -1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (check(mid, z, x))
		{
			d = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	//cout<<"d="<<d<<" x="<<x<<" z="<<z<<endl;
	//cout<<"val="<<q[z][d].val<<" id="<<q[z][d].idx<<endl;
	if (d == -1)return -1;
	else return (x - q[z][d].val) * n + q[z][d].idx;
}
void work()
{
	ma.clear();
	tot = 0;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i];
		s[i] += s[i - 1];
	}
	ll res = s[n];
	int tag = 0;
	if (res < 0)
	{
		tag = 1;
		res = -res;
		for (int i = 1; i <= n; i++)s[i] = -s[i];
	}
	if (res == 0)
	{
		for (int i = 1; i <= n; i++)
		{
			if (!ma[s[i]])ma[s[i]] = i;
		}
		for (int i = 1; i <= m; i++)
		{
			ll x; cin >> x;
			if (x == 0)
			{
				cout << 0 << endl;
				continue;
			}
			if (ma[x])cout << ma[x] << endl;
			else cout << -1 << endl;
		}
	}
	else
	{
		for (int i = 1; i <= n; i++)
		{
			ll x = s[i] / res, ss = (s[i] % res + res) % res;
			if (s[i] < 0)
			{
				if ((-s[i]) % res != 0)x--;
			}
			if (!ma[ss])
			{
				ma[ss] = ++tot;
				q[tot].clear();
				q[tot].pb({ x,i });
			}
			else
			{
				q[ma[ss]].pb({ x,i });
			}
		}
		for (int i = 1; i <= tot; i++)
		{
			sort(q[i].begin(), q[i].end(), cmp);
			//for(auto s:q[i])cout<<i<<" "<<s.val<<" "<<s.idx<<endl;
		}
		for (int i = 1; i <= m; i++)
		{
			ll x;
			cin >> x;
			if (tag)x = -x;
			if (x == 0)
			{
				cout << 0 << endl;
				continue;
			}
			ll y = x / res, ss = (x % res + res) % res;
			//cout<<"ss="<<ss<<endl;
			if (x < 0)
			{
				if ((-x) % res != 0)y--;
			}
			if (!ma[ss])cout << -1 << endl;
			else cout << query(y, ma[ss]) << endl;
		}
	}
}
int main() {
	std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int _ = 1;
	cin >> _;
	while (_--)work();
	return 0;
}

1011

题意:给定一个n,接下来n-1行,输入u,v表示u,v之间有一条边。然后n个数表示 i 节点的大小。然后定义如果一条简单路径 ( u − > v ) (u->v) (u>v) v v v的大小是这条路径上最大的,那么u就可以跳到到v。输出n行,表示从第 i 个节点出发,至多能走几个点。

思路:我们就直接按大小排序,从最小的值开始,然后去遍历这个点的所有邻边,如果邻接点出现过(较小值),就把当前点作为邻接点的父亲,新建一棵树。然后每个节点的答案就是当前节点的深度(根结点为1)

我是直接带权并查集记st直接算,或者直接新建一棵树算深度都行。

#include<bits/stdc++.h>
#define pll pair<int,int>
#define line pair<double,double>
#define mp make_pair
#define pb push_back
#define ll long long
#define INF 123456789123456789
#define inf 0x3f3f3f3f
#define pi acos(-1)
#define db(x) cout<<x<<" "
#define dl(x) cout<<x<<endl
#define ls now<<1
#define rs now<<1|1
using namespace std;
const int mod = 998244353;
const int MAXN = 2e5 + 10;
const int N = 1e5 + 10;
const double eps =1e-6;
int n, m;
struct node{
    int t,nex;
}e[MAXN];
int head[N],cnt;
void add(int a,int b)
{
    e[++cnt].nex=head[a];e[cnt].t=b;head[a]=cnt;
}
int st[N],num[N],fa[N];
int find(int x)
{
    if(fa[x]==x)return fa[x];
    int fx=find(fa[x]);
    st[x]+=st[fa[x]];
    return fa[x]=fx;
}
pll f[N];
int vs[N];
bool cmp(pll a,pll b)
{
    return a.first<b.first;
}
void dfs(int u)
{
    int b=find(u); 
    ll res=0; 
    for(int i=head[u];i;i=e[i].nex)
    {
        int v=e[i].t;
        if(!vs[v])continue;
        int a=find(v);
        fa[a]=b;
        st[a]+=num[b];
        res+=num[a];
        num[a]=0;
    }
    num[b]+=res;
}
void work()
{
    cin>>n;
    cnt=0;
    for(int i=1;i<=n;i++)
    {
        head[i]=0,fa[i]=i,num[i]=1,st[i]=0,vs[i]=0;
    }
    for(int i=1;i<n;i++)
    {
        int a,b;cin>>a>>b;
        add(a,b);add(b,a);
    }
    for(int i=1;i<=n;i++)
    {
        cin>>f[i].first;
        f[i].second=i;
    }
    sort(f+1,f+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        dfs(f[i].second);
        vs[f[i].second]=1;
    }
    for(int i=1;i<=n;i++)
    {
        find(i);
    }
    for(int i=1;i<=n;i++)
    {
        cout<<st[i]+1<<endl;
    }
}
int main() {
    std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _ = 1;
    cin >> _;
    while (_--)work();
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值