【题目记录】——ICPC徐州2019


题目集地址 2019ICPC徐州站

A Cat 规律

题目地址A Cat
题目大意:有 1 0 1 8 10^18 1018只猫站成一排,第i只猫的花费为i,下标为i,现在要买一个连续区间内的所有猫,初始资金为S,如果买区间[x,y]的所有猫,花费为 x ⨁ ( x + 1 ) ⋯ ⨁ y x\bigoplus(x+1)\dots\bigoplus y x(x+1)y,现在有T个询问,每个询问给出一个区间[L,R],代表需要在这个区间内购买,询问在这个区间内最多能买几只猫。
思路:这里面存在一个规律,以偶数开头的连续4个数相异或的结果是0,我们当时没有找到这个规律,根本无从下手。下次遇到这种情况应该打表尝试一下找规律的
有了这个规律就比较容易了,对于给定需要查询的区间,判断有多少个区间以偶数开头且长度为4的区间即可,把这样的子区间去掉然后暴力剩下的元素即可。
AC代码:

#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
int T,s,l,r;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>T;
    while(T--) {
        int res=-1;
        cin >>l>>r>>s;
        if(r-l+1<=4) {//如果区间长度小于4
            for(int i=l; i<=r; i++)
                for(int j=i; j<=r; j++) {
                    int sum=0;
                    for(int k=i; k<=j; k++)
                        sum^=k;
                    if(sum<=s&&j-i+1>res)
                        res=j-i+1;
                }
        } else {
            int cnt=0,L[10],R[10];
            //为了方便处理,把每个点都作为区间,最多有5个数不在0区间内
            if(l%2) {//如果左边界为奇数
                L[++cnt]=l;//录入
                R[cnt]=l;
                int len=r-l,num=len/4;//获得长度为4的个数
                L[++cnt]=l+1;//记录区间左右端点
                R[cnt]=l+num*4;
                for(int i=l+1+num*4; i<=r; i++) {//处理区间末尾剩下的
                    L[++cnt]=i;
                    R[cnt]=i;
                }
            } else {
                int len=r-l+1,num=len/4;//以左边界开始,获得长度为4的个数
                L[++cnt]=l;//记录区间左右端点
                R[cnt]=l+num*4-1;
                for(int i=num*4+l; i<=r; i++) {//处理区间末尾剩下的
                    L[++cnt]=i;
                    R[cnt]=i;
                }
            }
            for(int i=1; i<=cnt; i++)
                for(int j=i; j<=cnt; j++) {
                    int sum=0;
                    for(int k=i; k<=j; k++)
                        sum^=L[k]!=R[k]?0:L[k];//记录区间异或和,左右不等表示为区间
                    if(sum<=s&&R[j]-L[i]+1>res)
                        res=R[j]-L[i]+1;//记录最值
                }
        }
        cout <<res<<endl;
    }
    return 0;
}

C < 3 numbers

题目地址C ❤️ numbers
题目大意:T个询问,每次询问一个区间[L,R],设区间内素数个数为x,判断 x R − L + 1 < 1 / 3 \frac{x}{R-L+1}\lt 1/3 RL+1x<1/3是否成立
思路:素数密度,打表找几项就会发现,只要区间长度大于40的话就是Yes,剩下的直接暴力判断就行,注意在这里1是素数,需要特判。

#include<bits/stdc++.h>
using namespace std;
int prime(int m)
{
    int i;
    if(m==0)
        return 0;
    if(m==1)
        return 1;
    if(m==2)
        return 1;
    for(i=2; i<=sqrt(m)+1; i++)
    {
        if(m%i==0)
            return 0;
    }
    return 1;
}

int main()
{
    int t;
    scanf("%d",&t);
    int n,m;
    int cnt;
    while(t--)
    {
    	scanf("%d%d",&n,&m);
        int len=m-n+1;
        cnt=0;
        if(len>49)
        {
            printf("Yes\n");
            continue;
        }
        for(int i = n;i<= m;i++)
        {
            if(prime(i))
            cnt++;
        }
        if(cnt*3<len)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

F The Answer to the Ultimate Question of Life, The Universe, and Everything.

题目地址F The Answer to the Ultimate Question of Life, The Universe, and Everything.
题目大意:给出一个整数 x ( x ∈ [ 0 , 200 ] ) x(x\in[0,200]) x(x[0,200]),求一组整数解 a , b , c ( ∣ a ∣ , ∣ b ∣ , ∣ c ∣ ≤ 5000 ) a,b,c(|a|,|b|,|c|\le5000) a,b,c(a,b,c5000)满足方程 a 3 + b 3 + c 3 = x a^3+b^3+c^3=x a3+b3+c3=x
思路:x很小,可以直接预处理0 ~ 200的结果,然后对应x的值输出
关键就是打表查找满足x对应的三个数
打表代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
typedef long long ll;
tr1::unordered_map<ll,int> f;

ll g(ll x){
	for(ll i=-5000;i<=5000;i++)
		if (i*i*i==x) return i;

}

bool find(int x){
	for(ll i =0;i<=5000;i++)
			for(long long j=0;j<=5000;j++)
				if (f.count(i*i*i+j*j*j+x)){
					printf("a[%d] = %lld;\n",x,g(i*i*i+j*j*j+x));
					printf("b[%d] = %lld;\n",x,-i);
					printf("c[%d] = %lld;\n",x,-j);
					return true;
				}else if (f.count(i*i*i+j*j*j-x)){
					printf("a[%d] = %lld;\n",x,g(i*i*i+j*j*j-x)==0?0:-g(i*i*i+j*j*j-x));
					printf("b[%d] = %lld;\n",x,i);
					printf("c[%d] = %lld;\n",x,j);
					return true;
				}
	return false;
}

int main()
{
	freopen("ans.txt","w",stdout);
	f.clear();
	for(ll i=-5000;i<=5000;i++)
        f[i*i*i] = 1;
	for(int x=0;x<=200;x++){
		//printf("find %d\n",x );
		if (find(x)){
			printf("vis[%d] = true;\n",x);
		}
		else
			printf("vis[%d] = false;\n",x);
	}
	return 0;
}

L Loli, Yen-Jen, and a cool problem SAM(后缀自动机)

题目地址L Loli, Yen-Jen, and a cool problem
参考文章:Loli, Yen-Jen, and a cool problem(广义后缀自动机,裸题)
题目大意:给一颗字符串树,求从哪个结点向上L长度的字符串出现了多少次
思路:广义SAM模版题 记录一下每个点的父亲节点,每个点对应的状态节点,然后跑广义SAM,统计下数量,跳一下fail边就可以了。
AC代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 150;
const int kind = 26;
typedef long long ll;
int tot1 = 1, las = 1;
int ch[maxn * 2][kind],len[maxn * 2], fa[maxn * 2];
int cnt[maxn * 2],vis[maxn],d[maxn];
inline int newn(int x) { len[++tot1] = x; return tot1; }
inline int newnq(int p, int w) {
	int nq = newn(len[p] + 1);
	int q = ch[p][w];
	for (int i = 0; i < kind; i++)ch[nq][i] = ch[q][i];
	fa[nq] = fa[q];
	fa[q] = nq;
	while (p&&ch[p][w] == q)ch[p][w] = nq, p = fa[p];
	return nq;
}
void sam_ins(int c) {
	int p = las;
	if (ch[p][c]) {
		int q = ch[p][c];
		if (len[q] == len[p] + 1)las = q;
		else las = newnq(p, c);
		return;
	}
	int np = newn(len[las] + 1); las = tot1;
	while (p && !ch[p][c])ch[p][c] = np, p = fa[p];
	if (!p)fa[np] = 1;
	else {
		int q = ch[p][c];
		if (len[q] == len[p] + 1) fa[np] = q;
		else {
			fa[np] = newnq(p, c);
		}
	}
}
int dd[maxn * 2],who[maxn * 2];
void updatecount() {
	for (int i = 1; i <= tot1; i++)dd[len[i]]++;
	for (int i = 1; i <= tot1; i++)dd[i] += dd[i - 1];
	for (int i = 1; i <= tot1; i++)who[dd[len[i]]--] = i;
	for (int i = tot1; i >= 2; i--)cnt[fa[who[i]]] += cnt[who[i]];
}
int calc(int s,int l) {while (len[fa[s]]>=l){s = fa[s];}return cnt[s];}
char s[maxn];
int main() {
	int n, q;
	scanf("%d%d", &n, &q);
	scanf("%s", s);
	for (int i = 2; i <= n; i++)scanf("%d", &vis[i]);
	sam_ins(s[0] - 'A');
	d[1] = las;
	cnt[las]++;
	vis[1] = 1;
	for (int i = 2; i <=n; i++) {
		las = d[vis[i]];
		sam_ins(s[i-1] - 'A');
		cnt[las]++;
		d[i] = las;
	}
	updatecount();
	for (int i = 0; i < q; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		printf("%d\n", calc(d[a], b));
	}
}

M Kill The Tree 树的重心

题目地址M Kill The Tree
题目大意:给你一棵树,以1为根,你需要求出以每个节点作为根的子树的重心,按照升序输出。
思路:首先我们需要知道树的重心的一些性质。
1.树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心,他们的距离和一样。
2.把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
3.一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
4.一棵树最多有两个重心,且相邻。
这道题目主要用到了1、2、4三条性质。你还可以发现多颗子树和一个新的根节点合并时形成的新的树的重心在最重子树(节点最多的子树)的重心到根的链上。那么主要就是写一个dfs在当前树的子树的重心都被找到的时候,先找到最重的子树,一步步向父节点移。具体看代码。

vector<int> vi[maxn],ans[maxn];
int father[maxn],size[maxn];//father记录父节点,size记录以当前节点为根的子树的节点数
void dfs(int u,int fa){
	father[u]=fa;
	size[u]=1;
	int ma=0,index=0;
	for(int i=0;i<vi[u].size();i++){
		if(vi[u][i]==fa) continue;
		dfs(vi[u][i],u);
		size[u]+=size[vi[u][i]];//加上每一个子树的size
		if(size[vi[u][i]]>ma)//找到最大的子树
		{
			ma=size[vi[u][i]];
			index=vi[u][i];
		}
	}
	if(!index){//没有子树说明递归到了叶子节点,当前节点就是重心
		ans[u].pb(u);
		return ;
	}
	int temp=ans[index][0];//初始化为最大子树的重心
	int flag=1;
	while(size[temp]<=size[u]-size[temp]){//判断能否向父节点移动
		if(size[temp]==size[u]-size[temp])//移动后距离和不变,说明这两个都是重心 
		{
			ans[u].pb(temp);
			ans[u].pb(father[temp]);
			sort(ans[u].begin(),ans[u].end());
			flag=0;
			break;
		}
		temp=father[temp]; //更新当前重心
	}
	if(flag)
	ans[u].pb(temp);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值