2021CCPC网络赛题解(1001/1009/1007/1002/1006/1012/1011/1008)

1001.Cut The Wire(模拟)

1001.Cut The Wire
由题意:
1.当x为偶数时,与x/2进行连边
2.当x为奇数时,与3*x + 1连边
现在我们需要计算有多少条边(a, b),a <= n && b > n
模拟即可

#include
#define ll long long
using namespace std;
int main(){
//	freopen("2.in","r",stdin);
	int t;
	cin >> t;
	while(t--){
		ll n;
		cin >> n;
		//连边
		//我先把偶数进行计算完 就是 > n的偶数跟 <= n进行连边 
		ll ans = n / 2 + (n % 2 != 0);
		//计算奇数的边界
		// 3x + 1 = y
		// x = (y - 1)/3;
		//[x , n]之间的奇数
		ll size = (n - 1) / 3;
		//之后需要减去与偶数之间重复的地方即可 
		ans += (n - (size + (size % 2 == 1)))/2 + (n % 2) ;
		cout << ans << endl;
	}
}

1009.Command Sequence(模拟)

1009.Command Sequence
由题意:
给你一个移动序列包含四种字母(U向上 D向下 L向左 R向右)的字符串str,和长度n.
让你求有多少种子序列,使得一个点经过这个子序列的操作能够回到原点,例如UDLR就是绕了一周刚好回到原点此时答案+1
利用map即可,我们可以定起始点为(0, 0)之后就可以通过str的操作对(x,y)点进行++,之后到达的点(x,y)的mp[{x,y}]!=0那么就答案加上即可

#include
#define ll long long
using namespace std;
int main(){
//	freopen("2.in","r",stdin);
	int t;
	cin >> t;
	while(t--){
		int n;
		string str;
		cin >> n >> str;
		//U向上 D向下 L向左 R向右
		int x = 0, y = 0;
		map< pair<int,int>, int>mp;
		mp[make_pair(x,y)]++;
		ll ans = 0;
		for(int i = 0; i < n; i++){
			if(str[i] == 'U')x--;
			else if(str[i] == 'D')x++;
			else if(str[i] == 'L')y--;
			else y++;
			if(mp.count(make_pair(x, y)))ans += mp[make_pair(x,y)];
			mp[make_pair(x,y)]++;
		}
		cout << ans << endl;
	}
} 

1007.Function(二次函数性质 + 模拟)

1007.Function
2021CCPC网络赛题解(1001/1009/1007/1002/1006/1012/1011/1008)_第1张图片
由题意:给你一个g(x)的函数定义,之后给你一个由A,B,C,D,x,g(x)等未知数组成的f(x)函数,我们要求f(x)的最小值,有T组例子,每次给你A,B,C,D,N要你求最小值。
解法:我们可以观察n的范围是1e6,所以我的g(x)的范围是[1, 54],我们可以预处理出g(x)值对应的下标i,之后我们对函数f(x)进行转化
f(x) = (Ag(x) + B)x^2 + (Cg(x)^2 + Dg(x))x;
很明显这是一个二次函数形式,我们又直到g(x)只有[1,54]的范围所以我们直接分情况讨论二次函数即可

#include
#define ll long long
using namespace std;
const int maxn = 1e6 + 10;
vector<ll>g[maxn];
int exchange(int x){
	int cnt = 0;
	while(x){
		cnt = cnt + x % 10;
		x /= 10;
	}
	return cnt;
}
void solve(){
	ll a,b,c,d,n;
	cin >> a >> b >> c >> d >> n;
	ll mx = LLONG_MAX;//long long的最大值
	//f(x) = (Ag(x) + B)x^2 + (Cg(x)^2 + Dg(x))x;
	for(ll j = 1; j <= 54; j++){
		ll a1 = a*j + b;//二次函数的开口 
		ll b1 = c*j*j + d*j;//
		if(a1 > 0){
			//开口向上 我们就要求对称轴上或者两边的点就是最小的
			ll x = -b1/(2*a1);//对称轴
			int fin = lower_bound(g[j].begin(),g[j].end(), x) - g[j].begin();
			if(fin == g[j].size()){
				//找到了末尾
				//找第一个小于n的点 
				fin = upper_bound(g[j].begin(), g[j].end(), n) - g[j].begin() - 1;
				if(fin >= 0){
					mx = min(mx, a1*g[j][fin]*g[j][fin]+b1*g[j][fin]);
				} 
			} else{
				if(g[j][fin] > n){
					fin = upper_bound(g[j].begin(), g[j].end(), n) - g[j].begin() - 1;
					if(fin >= 0)
					mx = min(mx, a1*g[j][fin]*g[j][fin]+b1*g[j][fin]);
				}else{
					mx = min(mx, a1*g[j][fin]*g[j][fin]+b1*g[j][fin]);
					if(fin > 0)
					 mx = min(mx, a1*g[j][fin - 1]*g[j][fin - 1]+b1*g[j][fin - 1]);
					if(fin < g[j].size() - 1)
					mx = min(mx, a1*g[j][fin + 1]*g[j][fin + 1]+b1*g[j][fin + 1]);
				}
			}
		}else if(a1 == 0){
			//此时一个直线
			if(b1 >= 0){
				if(g[j].size() > 0 && g[j][0] <= n)
				mx = min(mx, a1*g[j][0]*g[j][0]+b1*g[j][0]); 
			} else{
				int x = upper_bound(g[j].begin(), g[j].end(), n) - g[j].begin() - 1;
				if(x >= 0)
				mx = min(mx, a1*g[j][x]*g[j][x]+b1*g[j][x]); 
			}
		}else{
			//开口向下 左右端点即可 
			if(g[j].size() > 0 && g[j][0] <= n)
				mx = min(mx, a1*g[j][0]*g[j][0]+b1*g[j][0]); 
			int x = upper_bound(g[j].begin(), g[j].end(), n) - g[j].begin() - 1;
			if(x >= 0)
			mx = min(mx, a1*g[j][x]*g[j][x]+b1*g[j][x]); 
		}
	}
	cout << mx << endl; 
}
int main(){
//	freopen("2.in","r",stdin);
	for(int i = 1; i <= 1e6; i++){
		int x = exchange(i);
		g[x].push_back(i);
	}
	int t;
	cin >> t;
	while(t--)solve();
}

1002.Time-division Multiplexing(双指针+数组模拟)

1002.Time-division Multiplexing
题意:给你n个字符串,让你以它的方式进行构造一个字符串
例子:
2
abc
bd
先把长度都变成n个字符串长度的lcm
abcabc
bdbdbd
那么构造出来的字符串str为abbdcbadbbcd
之后然我们求在str中(str可以进行多次与本身连接即循环节),求最小长度的子串使得包含n个字符串的每一种元素。

我只需要先把这个串构造出来,然后利用双指针进行移动就能求出来答案

#include
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
ll gcd(ll a, ll b){
	return b?gcd(b, a%b):a;
}
ll lcm(ll a, ll b){
	return a/gcd(a,b)*b;
}
string str[105];
int mp[26],cnt[26];
void solve(){
	int n;
	cin >> n;
	memset(mp, 0, sizeof mp);
	memset(cnt, 0, sizeof cnt);
	int len = 1;
	for(int i = 0; i < n; i++){
		cin >> str[i];
		int s = str[i].size();
		len = lcm(len, s);
		for(int j = 0; j < s; j++){
			mp[str[i][j] - 'a']++;
		}
	}
	string res = "";
	for(int i = 0; i < len; i++){
		for(int j = 0; j < n; j++)
		res += str[j][i % str[j].size()];
	}
	int sz = 0;
	for(int i = 0; i < 26;  i++)if(mp[i])sz++;
	res += res;
	int ans = 0x3f3f3f3f;
	int l = 0;
	int flag = 0;
	for(int i = 0; i < res.size(); i++){
		int x = res[i] - 'a';
		cnt[x]++;
		if(cnt[x] == 1){
			flag ++;
			//包含了全部元素 
			if(flag == sz){
				ans = min(ans,i - l + 1);
				while(true){
					int x = res[l] - 'a';
					cnt[x]--;
					if(cnt[x] == 0){
						l ++ ;
						flag--;
						break;
					}else{
						l++;
						ans = min(ans, i - l + 1);
					}
				}
			}
		}
	}
	cout << ans << endl;
}
int main(){
//	freopen("2.in","r", stdin);
	IOS;
	int t;
	cin >> t;
	while(t--)solve();
} 

1006.Power sum(构造)

1006.Power sum
2021CCPC网络赛题解(1001/1009/1007/1002/1006/1012/1011/1008)_第2张图片
由题意:让你构造出k个数使得题目的方程式之和=n
我们可以观察到a_i的值只能为1或-1
之后我们对i^2进行考虑,我们可以列出来
1 4 9 16 25 36 …
考虑差值 3 5 7 9 11 13 …
那么我们如果要构造出4可以用四个数构造出来(5 - 3) + (9 - 7)
那么我们就可以对n % 4进行分情况讨论就可以了

#include
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
void solve(){
    int n;
    cin >> n;
    //每产生4我们可以用四个数得到
    int x = n/4;
    int y = n%4;
    string ans = "";
    for(int i = 1; i <= x; i++){
    	ans += "1001";
	}
    if(y == 0){
    cout << n << endl; 
    } else if(y == 1){
        cout << n << endl;
        ans = "1" + ans;  
    }else if(y == 2){
        cout << n + 2 << endl;
        ans = "0001" + ans;     
    }else{
        cout << n - 1 << endl;
        ans = "01" + ans;    
    }
    cout << ans << endl;
}
int main(){
//    freopen("2.in","r",stdin);
    IOS;
    int t;
    cin >> t;
    while(t--)solve();
}

1012.Remove(数论的亿点点思路)

1012.Remove
2021CCPC网络赛题解(1001/1009/1007/1002/1006/1012/1011/1008)_第3张图片
题意:给你一个质数集合P和一个整数n,n表示有n个石头,每一次操作你可以选择质数P的集合的一个元素,之后你可以在这堆石头(剩余m)里面取m % p[i]个石头,你需要用最小的回合进行拿掉石子,答案按照题意构造方式进行处理出来
解法:我们可以先把质数集合进行排序,之后我们可以倒过来考虑,假设当前剩余石子为m,当前操作质数为p,那么我们前一次操作的数就为[x + 1, x + p - 1],通过这样逆推我们可以得到a_1 - a_n之后再进行逆过来求解就好了

#include
#define sc scanf
#define pr printf
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define INF 0x7fffffffffffffff
#define inf 0x3f3f3f3f
#define ull unsigned long long
using namespace std;
const int maxn = 2e6 + 5;
int p[maxn];
int a[maxn];
void solve(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)p[i] = a[i] = 0;
    for(int i = 1; i <= m; i++)scanf("%d",&p[i]);
    sort(p+1,p+1+m);
    int r = 0;
    for(int l = 0; l <= n;l = r,r = 0){
        for(int i = 1; i <= m; i++){
            int flag = (l/p[i])*p[i] + p[i] - 1;
            if(r < flag)r = flag;
        }
        for(int i = l+1; i <= r; i++)
        a[i] = a[l] + 1;
        if(l == r)break;
    }
    ull ans = 0,res = 1;
    for(int i = n; i >= 1; i--){
        ans = ans + a[i]*res;
        res *= 23333;
    }
    printf("%llu\n",ans);
}
int main(){
//    freopen("2.in","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)solve();
}

1011.Shooting Bricks(多维度dp)

1011.Shooting Bricks
洛谷有原题而且感觉洛谷讲解的很好^ _ ^所以给大家提供链接去洛谷学习一下有很多种写法洛谷题解链接

#include
#define sc scanf
#define pr printf
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define INF 0x7fffffffffffffff
#define inf 0x3f3f3f3f
#define ull unsigned long long
using namespace std;
const int maxn = 205;
int dp[maxn][maxn][2];
int dp1[maxn][maxn][2];
int a[maxn][maxn],vis[maxn][maxn];
void solve(){
    int n,m,k;
    memset(dp,0,sizeof(dp));
    memset(vis,0,sizeof(vis));
    memset(dp1,0,sizeof(dp1));
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            char op[3];
            scanf("%d%s",&a[i][j],op);
            if(op[0]=='Y')
                vis[i][j]=1;
        }
    }

    for(int i=1;i<=m;i++){
        //cnt表示实际用的子弹个数
        int cnt=0;
        for(int j=n;j>=1;j--){
            if(vis[j][i])
                dp[i][cnt][0]+=a[j][i];
            else{
                cnt++;
                dp[i][cnt][0]=dp[i][cnt-1][0]+a[j][i];
                dp[i][cnt][1]=dp[i][cnt-1][0]+a[j][i];
            }
        }
    }

    for(int i=1;i<=m;i++){
        for(int j=0;j<=k;j++){
            //p表示第i列可以用p颗子弹
            for(int p=0;p<=j&&p<=n;p++){
            //这个循环的意思是,给第i列分配p颗子弹,给前i-1列分配j-p颗子弹
                dp1[i][j][0]=max(dp1[i][j][0],dp1[i-1][j-p][0]+dp[i][p][0]);
                if(p){
                    //如果p>=1,那么第i列可以借一些子弹给前i-1列
                    dp1[i][j][1]=max(dp1[i][j][1],dp1[i-1][j-p][0]+dp[i][p][1]);
                }
                if(j-p){
                    //如果j-p>=1,那么前i-1列可以借一些子弹给第i列
                    dp1[i][j][1]=max(dp1[i][j][1],dp1[i-1][j-p][1]+dp[i][p][0]);
                }
            }
        }
    }
    printf("%d\n",dp1[m][k][1]);
}
int main(){
//    freopen("2.in","r",stdin);
    int t;
//    scanf("%d")
    scanf("%d",&t);
    while(t--)solve();
}

1008.GCD on Sequence(线段树 + 质数)

1008.GCD on Sequence
2021CCPC网络赛题解(1001/1009/1007/1002/1006/1012/1011/1008)_第4张图片
由题意:定义v(l, r) = maxl≤i
2021CCPC网络赛题解(1001/1009/1007/1002/1006/1012/1011/1008)_第5张图片

#include
#define ll long long
#define ls u<<1
#define rs u<<1|1
#define sc scanf
#define pr printf
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
const int maxn = 1e5 + 5;
ll a[maxn],pos[maxn],ans[maxn];
vector<int> fac[maxn];
void init(int n){
	for(int i = n; i >= 1; i--){
		for(int j = i; j <= n; j += i){
			fac[j].push_back(i);
		}
	}
}
struct Segtree{
	ll sum,min,max;
	pii laz;
}tr[maxn << 2];
void pushup(int u){
	tr[u].min = min(tr[ls].min,tr[rs].min);
	tr[u].max = max(tr[ls].max,tr[rs].max);
	tr[u].sum = tr[ls].sum + tr[rs].sum;
}
void modify(int u, int l, int r, pii laz){
	tr[u].laz = laz;
	tr[u].sum = (r - l + 1)*1ll*laz.y;
	tr[u].max = tr[u].min = laz.x;
}
void pushdown(int u, int l, int r){
	if(tr[u].laz.x){
		int mid = l + r >> 1;
		modify(ls, l, mid, tr[u].laz);
		modify(rs, mid+1, r, tr[u].laz);
		tr[u].laz.x = 0; 
	}
}
void update(int u, int l, int r,int x, int y, int z, int i){
	if(tr[u].min >= z)return ;
	if(x <= l && r <= y && tr[u].max == tr[u].min){
		ans[tr[u].max] += 1ll*i*(r - l + 1) - tr[u].sum;
		modify(u, l, r, make_pair(z, i));
		return ;
	}
	pushdown(u, l, r);
	int mid = l + r >> 1;
	if(x <= mid)update(ls, l, mid, x, y, z, i);
	if(y > mid)update(rs, mid + 1, r, x, y, z, i);
	pushup(u);
}
void solve(){
	int n;
	sc("%d",&n);
	for(int i = 1; i <= n * 4; i++){
		if(i <= n + 1)ans[i] = pos[i] = 0;
		tr[i].laz.x = tr[i].laz.y = tr[i].max = tr[i].min = tr[i].sum = 0;
	}
	for(int i = 1; i <= n; i++){
		sc("%lld",&a[i]);
		vector<int>s;
		for(int j = 0; j < fac[a[i]].size(); j++){
			if(pos[fac[a[i]][j]]){
				if (s.empty() || pos[s.back()] < pos[fac[a[i]][j]]) s.push_back(fac[a[i]][j]);
			} 
		}
		int last = 0;
		for(int j = 0; j < s.size(); j++){
			int x = s[j];
			update(1, 1, n, last + 1, pos[x], x, i);
			last = pos[x];
		}
		for(int j = 0; j < fac[a[i]].size(); j++){
			pos[fac[a[i]][j]] = i;
		}
	}
	update(1, 1, n, 1, n, n + 1, n + 1);
	for(int i = 1; i <= n; i++){
		printf("%lld\n",ans[i]);
	}
	
}
int main(){
//	freopen("2.in","r",stdin);
	init(1e5);
	int t;
	sc("%d",&t);
	while(t--)solve();
}

你可能感兴趣的:(CCPC补题,算法)