2020ICPC 江西省大学生程序设计竞赛(A,B,E,G,H,I,K,L,M)

judge: 牛客node

A-A Simple Math Problem

judge:牛客ios

题意

给你一个n,让你求出\(\sum_{i=1}^{n}\sum_{j=1}^{i}[gcd(i,j)==1]f(j)\)
其中f(x)表示的是数位和,eg:f(122)=1+2+2=5。c++

题解

一眼能够看出是道反演题,可是仔细想一想发现不是特别好维护,而后给的范围又有点误导,让人觉得能够瞎搞过(实际上真的能够打表过或者容斥过),而后中间耽搁了很长时间,还写了个瞎搞的作法,不过没敢交,最后才发现转换一下就是一道经典的反演题。
首先题目让咱们求的是\(\sum_{i=1}^{n}\sum_{j=1}^{i}[gcd(i,j)==1]f(j)\),咱们须要转换成\(\sum_{i=1}^{n}f(i)\sum_{j=i+1}^{n}[gcd(i,j)==1]\)
其实只是枚举策略的变换,求的答案仍是同样,只不过第二个公式能够用反演求,并且还比较好求,第一个没有办法用反演作(实际上是我不会)。
至于缘由,咱们须要对第一个公式有足够的了解,第一个公式让咱们求的是全部比当前数小,且与当前数互质的数的数位和。
思惟转换一下,咱们把每一个数单独进行考虑,每一个数对答案产生的贡献就是比它大,且与它互质的数的个数乘以这个数的数位和(就是转换后的公式)。
至于第二个公式怎么求,能够参考我写的一篇题解,这个题让咱们求的就是前半部分,只不过数位和变成了数位乘。app

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (int i = (s); i <= (t); i++)
#define RP(i,t,s) for (int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
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;
}
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 1e6+7;
int n;
ll phi[N],mu[N];
int prime[N];
int flag[N];
ll F[N];
ll G[N];
ll Num[N];
int num=0;
int f(int x){
	int ans=0;
	while(x) ans+=x%10,x/=10;
	return ans;
}
int g(int x){
	int ans=1;
	while(x) ans*=x%10,x/=10;
	return ans;
}
void init(){
	phi[1]=1;
	mu[1]=1;
	F[1]=G[1]=1;
	for (int i=2;i<=n;i++){
		F[i]=f(i);
		G[i]=g(i);
		if (flag[i]==0)//这表明i是质数 
		{
			prime[++num]=i;
			phi[i]=i-1;
			mu[i]=-1;
		}
		for (int j=1;j<=num&&prime[j]*i<=n;j++){
			flag[i*prime[j]]=1;
			if (i%prime[j]==0){
				phi[i*prime[j]]=phi[i]*prime[j];
				mu[i*prime[j]]=0;
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1),mu[i*prime[j]]=-mu[i];
		}
	}
    rp(i,1,n) for(int j=i;j<=n;j+=i) Num[i]+=F[j];
}
void solve(){
	n=read();
	init();
	ll ans1=0;
    ll ans2=0;
	rp(i,1,n){
		ll t=0;for(int j=i;j<=n;j+=i) t+=F[j];
		ans1+=mu[i]*t*(n/i);
	}
    rp(i,1,n) ans2+=F[i]*phi[i];
	cout<<ans1-ans2+1<<endl;
}
int main(){
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
	freopen("in.txt", "r", stdin);
	//debug = 1;
#endif
	time_t beg, end;
	//if(debug) beg = clock();
    solve();

	/*
	if(debug) {
		end = clock();
		printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
	}
	*/
	return 0;
}

B-Apple

judge:牛客ui

题意

现有n个苹果,求出可否分给m我的,知足全部人的苹果总数都不同。spa

题解

首先求出知足m我的的苹果数量都不一样的最少苹果数。最实惠的方案固然是依次拥有1,2,3,...,m-1,m个苹果。须要花费\(\frac{m(m+1)}{2}\)个苹果。至于剩下的苹果,所有交给最后一我的便可,这样就知足了全部人的苹果数都不同。.net

若是苹果总数小于\(\frac{m(m+1)}{2}\),就说明不管怎么分都没法知足每一个人的苹果总数都不同。debug

代码

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    int s = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
    return s * f;
}
void solve() {
    int n = read(), m = read();
    if (n >= m * (m + 1) / 2) puts("possible");
    else puts("impossible");
}
int main() {
    int T = read();
    while (T--) solve();
    return 0;
}

E-Color Sequence

judge:牛客code

题意

定义一个合法的序列为序列里的每一个元素都出现偶数次。blog

给定一个有n个元素的序列,序列元素最多有21种,编号在数值在[0-20],求出合法序列的个数。

题解

维护每一个数字出现次数的前缀和,且咱们只关心数字出现次数的奇偶性,因此咱们只保存 \(0\)\(1\) 两个状态,一个二进制位便可保存一个数字的状态。将 \(21\) 个前缀和的对应位置概括到一块儿,那么一个 \(int\) 类型的整数就可保存一组状态。

当一组状态为 \(t\) 时,维护每组状态出现的次数 \(num[t]\)

假设 \(a_i\)\(2\),前一个位置的状态为 \(t\),那么将 \(a_i\) 加入 \(t\),只须要将 \(t\) 中的第 \(i\) 个位置取反,即 \(t=t\oplus(1<<a_i)\)。而后将 \(num[t]=num[t]+1\)

假设将 \(a_i\) 加入后的状态为 \(t\),则前面有 \(num[t]\) 个位置能够做为起点,第 \(i\) 个位置做为终点,区间内全部元素的出现次数都为偶数。

\[ans=\sum_{i=1}^{n}{num[t_i]} \]

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 100005;
const int maxm = 5000005;
const int inf = 0x3f3f3f3f;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
int n;
int mp[maxm];

void sol() {
    int t = 0;
    int ans = 0;
    ++mp[t];
    _rep(i, 1, n) {
        int x = read();
        t ^= (1 << x);
        ans += mp[t];
        ++mp[t];
    }
    printf("%d\n", ans);
}

int main() {
    n = read();
    sol();
    return 0;
}

G-Mathematical Practice

judge:牛客

题意

题意不详...

题解

队友一眼看出答案是 \((m+1)^n\)...

代码

#include <bits/stdc++.h>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int mod = 998244353;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

LL quickPow(LL x, LL n, LL mod) {
    LL ans = 1;
    while(n) {
        if(n & 1) ans *= x, ans %= mod;
        x *= x, x %= mod;
        n >>= 1;
    }
    return ans;
}

int main() {
    LL n = read(), m = read();
    printf("%lld\n", quickPow(m + 1, n, mod));
    return 0;
}

H-Sequence

judge:牛客

题意

给你一个每一个元素都互不相同的长度为 \(n\) 的序列.

定义两种操做:

  1. 选择一个元素 \(a_x\), 令 \(a_x=y\).
  2. 选择一个元素 \(a_x\), 找出最小值为 \(a_x\) 的子串的个数.

题解

分块解法

考虑分块维护每一个块的最小值.

当执行操做1时, 令 \(a_x=y\), 同时从新计算 \(a_x\) 所在的块的最小值.

当执行操做2时, 分别找出左右两边连续大于等于 \(a_x\) 的元素的个数, 例如134562中4左右分别有1和2个不小于4的元素. 那么能够计算出包含4的子串的数目为 \(1+2+1\times 2+1=6\), 当左边有 \(cl\) 个元素,右边有 \(cr\) 个元素时,包含 \(a_x\) 的字串的数目为 \(cl+cr+cl\times cr+1\).

线段树解法1

线段树维护区间最小值。

当执行操做 \(1\) 时,\(update\) 执行更新。

当执行操做 \(2\) 时,分别找出 \([1,x-1]\)\([x+1,n]\) 中距离 \(x\) 最近的最小值的位置,也就是在 \([1,x-1]\) 中找到最靠右的小于 \(a[x]\) 的值的位置,在 \([x+1,n]\) 中找出最靠左的小于 \(a[x]\) 的值的位置。

当咱们查找 \([1,x-1]\) 中最靠右的小于 \(a[x]\) 的值时,应首先检查右儿子最小值是否小于 \(a[x]\),若是小于说明右儿子区间有可能有答案。

之因此说“有可能有答案”是由于线段树每次查找的区间 \([beg,end]\) 和咱们想要找的区间 \([l,r]\) 不必定是重合的,因此就算 \(T[node]\) 小于 \(a[x]\),也仅仅说明是 \([beg,end]\) 区间内有小于 \(a[x]\) 的值,而不是 \([l,r]\) 区间内。当最小值出如今 \(end\) 前面,\(r\) 后面时,只查找右儿子显然会犯错。

更合理的作法是每次查找先判断当前区间的最小值是否大于 \(a[x]\)。而后根据上面的要求查找右儿子,若是右儿子找到了合法的答案,那么左儿子就不必再访问了。若是右儿子没有找到合法的答案,那就须要再找左儿子。

查找 \([x+1,n]\) 同理。

这样单次查询就能直接搜出答案。时间复杂度 \(O(nlogn)\)

答案是 \(cl+cr+cl\times cr+1\)

线段树解法2

线段树维护区间最小值。

直接二分左右边界,查询区间最小值检验。

时间复杂度相比“线段树解法1”多了个 \(logn\),时间复杂度 \(O(nlog^2n)\)

代码

分块解法

#include <bits/stdc++.h>
#define _for(i, a) for(LL i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(LL i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const LL maxn = 100005;

inline LL read() {
    LL x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

LL a[maxn], k[maxn], len;
LL n, m;

LL getN(LL pos) {
    return (pos - 1) / len + 1;
}

void init() {
    len = sqrt(n) + 1;
    len = min(len, n);
}

LL getLValOfThis(LL pos) {
    LL nu = getN(pos);
    for(LL i = pos - 1; i > (nu - 1) * len; --i) if(a[i] < a[pos]) return i;
    return -1;
}
LL getRValOfThis(LL pos) {
    LL nu = getN(pos);
    for(LL i = pos + 1; i <= nu * len; ++i) if(a[i] < a[pos]) return i;
    return -1;
}
LL getLVal(LL pos) {
    LL nu = getN(pos);
    for(LL i = nu - 1; i > 0; --i) if(k[i] < a[pos]) return i;
    return -1;
}
LL getRVal(LL pos) {
    LL nu = getN(pos), mn = getN(n);
    for(LL i = nu + 1; i <= mn; ++i) if(k[i] < a[pos]) return i;
    return -1;
}

void sol() {
    init();
    _rep(i, 1, n) a[i] = read();
    LL mn = getN(n);
    _rep(i, 1, mn) k[i] = LLONG_MAX;
    _rep(i, 1, n) k[getN(i)] = min(k[getN(i)], a[i]);
    _for(i, m) {
        LL op = read();
        if(op == 1) {
            LL x = read(), val = read();
            LL nu = getN(x);
            a[x] = val;
            k[nu] = LLONG_MAX;
            for(LL i = (nu - 1) * len + 1; i <= nu * len; ++i) k[nu] = min(k[nu], a[i]);
        }
        else {
            LL pos = read();
            LL l = pos - 1, r = pos + 1;
            LL cl = 0, cr = 0;
            LL LThis = getLValOfThis(pos);
            if(LThis != -1) cl = pos - LThis - 1;   // 本块内找到
            else {                                  // 本块内未找到,从下一块开始找
                LL nu = getLVal(pos);
                if(nu == -1) cl = pos - 1;          // 左边没有比a[pos]更小的
                else {                              // 左边有比a[pos]更小的
                    cl = pos - nu * len - 1;
                    for(LL i = min(pos - 1, nu * len); i > 0 && a[i] > a[pos]; --i) {
                        cl = pos - i;
                    }
                }
            }
            LL RThis = getRValOfThis(pos);
            if(RThis != -1) cr = RThis - pos - 1;   // 本块内找到
            else {                                  // 本块内未找到,从下一块开始找
                LL nu = getRVal(pos);
                if(nu == -1) cr = n - pos;          // 右边没有比a[pos]更小的
                else {                              // 右边有比a[pos]更小的
                    cr = (nu - 1) * len - pos;
                    for(LL i = max(pos + 1, (nu - 1) * len + 1); i <= n && a[i] > a[pos]; ++i) {
                        cr = i - pos;
                    }
                }
            }
            LL ans = cl + cr + cl * cr + 1;
            printf("%lld\n", ans);
        }
    }
}

int main() {
    n = read(), m = read();
    sol();
    return 0;
}

线段树解法1

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 100005;

inline LL read() {
    LL x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n, m;
LL a[maxn];

LL T[maxn << 2];
void build(int node, int beg, int end) {
    if (beg == end) {
        T[node] = a[beg];
        return;
    }
    int mid = (beg + end) >> 1;
    build(node << 1, beg, mid);
    build(node << 1 | 1, mid + 1, end);
    T[node] = min(T[node << 1], T[node << 1 | 1]);
}
void update(int node, int beg, int end, int pos, LL val) {
    if(beg == end) {
        T[node] = val;
        return;
    }
    int mid = (beg + end) >> 1;
    if(pos <= mid) update(node << 1, beg, mid, pos, val);
    else update(node << 1 | 1, mid + 1, end, pos, val);
    T[node] = min(T[node << 1], T[node << 1 | 1]);
}
LL queryl(int node, int beg, int end, int l, int r, LL val) {
    if(T[node] > val) return 0;
    if (beg == end) return beg;
    LL ans = 0;
    int mid = (beg + end) >> 1;
    if(mid < r && T[node << 1 | 1] < val) ans = queryl(node << 1 | 1, mid + 1, end, l, r, val);
    if(mid >= l && ans == 0) ans = queryl(node << 1, beg, mid, l, r, val);
    return ans;
}
LL queryr(int node, int beg, int end, int l, int r, LL val) {
    if(T[node] > val) return n + 1;
    if (beg == end) return beg;
    LL ans = n + 1;
    int mid = (beg + end) >> 1;
    if(mid >= l && T[node << 1] < val) ans = queryr(node << 1, beg, mid, l, r, val);
    if(mid < r && ans == n + 1) ans = queryr(node << 1 | 1, mid + 1, end, l, r, val);
    return ans;
}
void sol() {
    _rep(i, 1, n) a[i] = read();
    build(1, 1, n);
    _for(i, m) {
        int op = read();
        if(op == 1) {
            int x = read(), val = read();
            a[x] = val;
            update(1, 1, n, x, val);
        }
        else {
            int x = read();
            LL cl = x - 1, cr = n - x;
            if(x > 1) cl = x - 1 - queryl(1, 1, n, 1, x - 1, a[x]);
            if(x < n) cr = queryr(1, 1, n, x + 1, n, a[x]) - x - 1;
            LL ans = cl + cr + cl * cr + 1;
            printf("%lld\n", ans);
        }
    }
}

int main() {
    n = read(), m = read();
    sol();
    return 0;
}

线段树解法2

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 100005;

inline LL read() {
    LL x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n, m;
LL a[maxn];

LL T[maxn << 2];
void build(int node, int beg, int end) {
    if (beg == end) {
        T[node] = a[beg];
        return;
    }
    int mid = (beg + end) >> 1;
    build(node << 1, beg, mid);
    build(node << 1 | 1, mid + 1, end);
    T[node] = min(T[node << 1], T[node << 1 | 1]);
}
void update(int node, int beg, int end, int pos, LL val) {
    if(beg == end) {
        T[node] = val;
        return;
    }
    int mid = (beg + end) >> 1;
    if(pos <= mid) update(node << 1, beg, mid, pos, val);
    else update(node << 1 | 1, mid + 1, end, pos, val);
    T[node] = min(T[node << 1], T[node << 1 | 1]);
}
int query(int node, int beg, int end, int l, int r) {
    if (l <= beg && r >= end) return T[node];
    int ans = 0x3f3f3f3f;
    int mid = (beg + end) >> 1;
    if(mid >= l) ans = min(ans, query(node << 1, beg, mid, l, r));
    if(mid < r) ans = min(ans, query(node << 1 | 1, mid + 1, end, l, r));
    return ans;
}
void sol() {
    _rep(i, 1, n) a[i] = read();
    build(1, 1, n);
    _for(i, m) {
        int op = read();
        if(op == 1) {
            int x = read(), val = read();
            a[x] = val;
            update(1, 1, n, x, val);
        }
        else {
            int x = read();
            LL cl = 0, cr = 0;
            if(x > 1) {
                LL l = 1, r = x - 1;
                while(l <= r) {
                    LL mid = (l + r) >> 1;
                    if(query(1, 1, n, mid, x - 1) > a[x]) {
                        cl = x - mid;
                        r = mid - 1;
                    }
                    else l = mid + 1;
                }
            }
            if(x < n) {
                LL l = x + 1, r = n;
                while(l <= r) {
                    LL mid = (l + r) >> 1;
                    if(query(1, 1, n, x + 1, mid) > a[x]) {
                        cr = mid - x;
                        l = mid + 1;
                    }
                    else r = mid - 1;
                }
            }
            LL ans = cl + cr + cl * cr + 1;
            printf("%lld\n", ans);
        }
    }
}

int main() {
    // freopen("in.txt", "r", stdin);
    n = read(), m = read();
    sol();
    return 0;
}

I-Simple Math Problem

judge:牛客

题意

给你一个 \(n\times n\) 的矩阵,其数字的排布方式以下:

0   1   3   6   10
2   4   7   11  15
5   8   12  16  19
9   13  17  20  22
14  18  21  23  24

求出第 \(x\) 行第 \(y\) 列的元素的值. 注意\((0≤x≤1000000000, 0≤y≤1000000000, 1≤n≤1000000001)\).

题解

旋转一下矩阵:

0
      2   1
    5   4   3
  9   8   7   6
14  13  12  11  10
  18  17  16  15
    21  20  19
      23  22
        24

那么第 \(x\) 行第 \(y\) 列就对应第 \(x+y-1\) 行. 假设 \(r=x+y-1\).

  1. \(x+y<=n\) 时, 先算出前 \(r-1\) 行的数字个数 \(1+2+...+r-1\), 等差数列求和: \(\frac{r(r-1)}{2}\), 再加上 \(r\) 行剩下的 \(x\) 个数, 因为题目从0开始,因此为 \(\frac{(x+y-1)(x+y-2)}{2}+x-1\).
  2. \(x+y-1>n\) 时, 一样利用上面的规律算出下面得数字个数,而后拿 \(n\times n\) 减去个数便可. 假设 \(x' = n - x + 1, y' = n - y + 1\) , \(a[x][y]\) 后面的数字个数为 \(\frac{(x'+y'-1)(x'+y'-2)}{2}+x'-1\), 那么 \(a[x][y]\) 就是正向的第 \(n\times n-\frac{(x'+y'-1)(x'+y'-2)}{2}-x'+1\) 个数, 从0开始就是 \(n\times n-\frac{(x'+y'-1)(x'+y'-2)}{2}-x'\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main() {
    LL n = read(), x = read() + 1, y = read() + 1, ans = 0;
    if(x + y <= n + 1) {
        ans = (x + y - 1) * (x + y - 2) / 2 + x - 1;
    }
    else {
        LL _x = n - x + 1, _y = n - y + 1;
        ans = (_x + _y - 1) * (_x + _y - 2) / 2 + _x;
        ans = n * n - ans;
    }
    printf("%lld\n", ans);
    return 0;
}

K-Travel Expense

judge:牛客

题意

有 n 座城市,m 条双向道路,一条道路要走一天,每条道路的费用是 n ^ m ( n 是所携带的物品数量, m 是第几天),给你一个出发城市 S ,目的地城市 T ,预算 B ,问最多能携带多少物品。

题解

先用floyed求出任意两个城市之间的最短距离。而后二分答案,check一下就好了。

代码

// #pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#define _for(i, a) for(register int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(register int i = (a), lennn = (b); i <= lennn; ++i)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 100005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
LL dis[110][110];
LL b;
inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

struct poi {

};

LL check(LL x,LL y){
	LL sum=0;
	LL flag=1;
	for(int i=1;i<=y;i++){
		flag*=x;
		sum+=flag;
		if(sum>b)
			return sum;
	}
	return sum;
}

void init() {
    for(int i=0;i<109;i++)
		for(int j=0;j<109;j++)
			dis[i][j]=110;
	ios::sync_with_stdio(0);
}

void sol() {
    init();
    LL n,m;
	cin>>n>>m;
	for(LL i=1;i<=m;i++){
		LL x,y;
		cin>>x>>y;
		dis[x][y]=dis[y][x]=1;
	}
	for(LL k=1;k<=n;k++)
        for(LL i=1;i<=n;i++)
            for(LL j=1;j<=n;j++)
                if(dis[i][j] > dis[i][k] + dis[k][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
	LL q;
	cin>>q;
	while(q--){
		LL s,t;
		cin>>s>>t>>b;
		LL l=0,r=1e9+10;
		LL ans=0;
		LL mid;
		while(l<r){
			mid=(l+r)/2;
			LL flag=check(mid,dis[s][t]);
			if(flag>=b){
				r=mid;
			}else{
				l=mid+1;
			}
			if(flag<=b){
				ans=max(ans,mid);
			}
		}
		cout<<ans<<endl;
	}	
}

int main() {
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();
    sol();
    return 0;
}

L-WZB's Harem

judge:牛客

题意

把n个皇后安排在一个 \(n\times n\) 的矩阵里,以便每行每列都有且仅有一个皇后,其中矩阵的某些位置不可用。

计算合法的方案数。

题解

状压dp。

\(n\) 个皇后编号为 \(1,2,...,n\)

假设 \(1\) 号皇后在第一行,\(2\) 号皇后在第 \(2\) 行,以此类推,\(n\) 号皇后在第 \(n\) 行。

保存每一列的皇后状态。假设 \(n\)\(2\),第 \(2\) 列已经被安排了皇后,那么状态就为0 1。因为 \(n\) 只有 \(20\),因此能够用一个 \(int\) 类型的整数保存每一列的状态,二进制位为 \(1\) 就表明这一列已经被安排了皇后。以后就能够考虑状态转移了。

假设 \(n\)\(4\)。依次枚举 \(1-n\) 号皇后,当 \(1\) 号皇后安排在第 \(1\) 列时,\(2\) 号皇后就能够被安排在第 \(2,3,4\) 列,那么1000就能够转移到1100,1010,1001。固然,这时是默认全部位置均可用,假设第 \(2\) 行第 \(3\) 列的位置不可用,那么1000就只能转移到1100,1001

设置0001,0010,0100,1000的方案数为 \(1\),求出全部的转移以后,1111表明的方案数就是基于“假设 \(1\) 号皇后在第一行,\(2\) 号皇后在第2行,以此类推,\(n\) 号皇后在第 \(n\) 行”的所有方案数。

答案乘以\(A_n^n\)就是最终的的方案数。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 25;
const int mod = 1000000007;

inline int read() {
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
int a[maxn][maxn];
LL dp[1 << 21];
LL ji[maxn];

void init() {
    _for(i, (1 << n)) dp[i] = 0;
}

int getN1(int i) {
    int num = 0;
    while(i) num += (i & 1), i >>= 1;
    return num;
}

void sol() {
    init();
    _for(i, n) _for(j, n) a[i][j] = read();
    _for(i, n) if(!a[0][i]) dp[1 << i] = 1;
    for(int i = 1; i < (1 << n); ++i) {
        int cnt = getN1(i);
        _for(j, n) {
            if(i & (1 << j)) continue;
            if(a[cnt][j]) continue;
            dp[i | (1 << j)] += dp[i];
            dp[i | (1 << j)] %= mod;
        }
    }
    printf("%lld\n", dp[(1 << n) - 1] * ji[n] % mod);
}

int main() {
    ji[1] = 1;
    for(int i = 2; i <= 21; ++i) ji[i] = ji[i - 1] * i % mod;
    n = read();
    sol();
    return 0;
}

M-Zoos's Animal Codes

judge:牛客

题意

签到题

每一个园子有个园子号,每一个动物有个动物号,合起来就是动物编号.

给出园子号和动物号,求出动物编号.

题解

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    string a, b;
    cin >> a >> b;
    cout << a << b;
}
相关文章
相关标签/搜索