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;
}
相關文章
相關標籤/搜索