2018牛客多校第一场

A

emmm,先贴一个没听一次的定理    Lindström–Gessel–Viennot lemma
然后存一下递推求组合数的模板

#include 
using namespace std;
 
typedef long long ll;

const ll mod = 1000000007;
const int N = 2500;
ll n, m;
ll C[N][N];

//递推求组合数 
void Init() {
    for(int i = 0; i < N; i ++) {
        for(int j = 0; j <= i; j ++) {
            if(j == 0 || j == i) {
                C[i][j] = 1;
            } 
			else {
                C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
            }
        }
    }
}
 
int main() {
    Init();
    while(~ scanf("%lld%lld", &n, &m)) {
        ll x = (C[n + m][n] * C[n + m][n]) % mod;
        ll y = (C[n + m][n + 1] * C[n + m][n - 1]) % mod;
        printf("%lld\n", ((x - y) % mod + mod) % mod);
    }
    return 0;
}

B

把这个矩阵看成是无向图的邻接矩阵,然后每个点的度为2,且没有自环,所以每个点属于且仅属于一个环

#include 
using namespace std;
 
typedef long long ll;
const ll maxn = 100100;
ll n, m;
ll d[maxn], f[maxn];
 
ll add(ll a, ll b) {
    ll ret = a + b;
    return ret > m ? ret - m : ret;
}
 
int main() { 
    while(~ scanf("%lld %lld", &n, &m)) {
        d[1] = 0;
        d[2] = d[3] = 1;
        f[3] = 1;
        for(ll i = 4; i <= n; i ++) {
            ll tmp = (i - 1) * (i - 2) / 2 % m;
            tmp = tmp * d[i - 3] % m;
            f[i] = add(tmp, (i - 1) * f[i - 1] % m);
            tmp = (i - 1) * d[i - 2] % m;
            d[i] = add(tmp, f[i]);
        }
        printf("%lld\n", d[n] % m);
    }
    return 0;
}

D

#include 
using namespace std;

int n, m1, m2, u[100], v[100], g1[10][10], g2[10][10], a[10];
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    while (cin >> n >> m1 >> m2) {
        memset(g1, 0, sizeof(g1));
        memset(g2, 0, sizeof(g2));
        for (int i = 1; i <= m1; i++) {
            cin >> u[i] >> v[i];
            g1[u[i]][v[i]] = g1[v[i]][u[i]] = 1;
        }
        for (int i = 1; i <= m2; i++) {
            int x, y;
            cin >> x >> y;
            g2[x][y] = g2[y][x] = 1;
        }
        for(int i=1;i<=n;i++) a[i]=i;
        int ans = 0, num = 0,kase=0;
        do {
            int flag1 = 1, flag2 = 1;
            for (int i = 1; i <= m1; i++) {
                int x = a[u[i]], y = a[v[i]];
                if (!g1[x][y])flag1 = 0;
                if (!g2[x][y])flag2 = 0;
            }
            ans += flag2;
            num += flag1;
        } while (next_permutation(a + 1, a + n + 1));
        cout << ans / num << "\n";
    }
}

E

#include 
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAX=1e5+10;
const ll mod=1e9+7;

ll dp[MAX][12];
int last[12],pre[MAX],a[MAX];

int main(){
	int n, m, k;
	while(~ scanf("%d %d %d", &n, &m, &k)){
		for(int i = 1; i <= k; i ++) 
			last[i] = 0;
		for(int i = 1; i <= n; i ++){
			scanf("%d", &a[i]);
			pre[i] = last[a[i]];
			last[a[i]] = i;
		}
		for(int i = 0; i <= m; i ++) 
			dp[i][i] = 1;
		for(int i = 1; i <= n; i ++){
			dp[i][0] = 1;
		for(int j = 1; j <= min(i - 1, m); j ++){
			dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1]) % mod;
			if(pre[i] && i - pre[i] <= j){
					dp[i][j] -= dp[pre[i] - 1][j - (i - pre[i])];
					dp[i][j] %= mod;
					dp[i][j] = (dp[i][j] + mod) % mod;
				}
			}
		}
		printf("%lld\n", dp[n][m]);
	}
	return 0;
}

F


#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const LL mod = 1000000007;
const int N = 1005;
 
LL C[N][N];
LL B[N],Inv[N];
LL Tmp[N];
LL n;
 
void Init()
{
    //预处理组合数
    for(int i=0; i

I

补不动,补不动,后缀自动机,什么鬼。。。

#include
#include
#include
#include
#include
 
#define rep(i,e) for(int i=0;i<(e);i++)
#define PB push_back
#define scd(a) scanf("%d",&a)
 
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int idx;
int maxlen[N], minlen[N], trans[N][27], slink[N];
int new_state(int _maxlen, int _minlen, int* _trans, int _slink) {
    maxlen[idx] = _maxlen;
    minlen[idx] = _minlen;
    for(int i = 0; i < 27; i++) {
        if(_trans == NULL)
            trans[idx][i] = -1;
        else
            trans[idx][i] = _trans[i];
    }
    slink[idx] = _slink;
    return idx++;
}

int add_char(char ch, int u) {
    int c = ch - 'a';
    int z = new_state(maxlen[u] + 1, -1, NULL, -1);
    while(u != -1 && trans[u][c] == -1) {
        trans[u][c] = z;
        u = slink[u];
    }
    if(u == -1) {
        minlen[z] = 1;
        slink[z] = 0;
        return z;
    }
    int x = trans[u][c];
    if(maxlen[u] + 1 == maxlen[x]) {
        minlen[z] = maxlen[x] + 1;
        slink[z] = x;
        return z;
    }
    int y = new_state(maxlen[u] + 1, -1, trans[x], slink[x]);
    minlen[z] = minlen[x] = maxlen[y] + 1;
    slink[z] = slink[x] = y;
    while(u != -1 && trans[u][c] == x) {
        trans[u][c] = y;
        u = slink[u];
    }
    minlen[y] = maxlen[slink[y]] + 1;
    return z;
}
 
int n;
char s[N];
char f[200]; // 存映射
vector ve;
void deal(int &st){
    rep(k,3) f[ve[k] + 'a'] = k + 'a';
    rep(i,n){
        st = add_char(f[s[i]], st);
    }
}
ll dp[N];
ll dfs(int st){
    ll& ret = dp[st];
    if(ret!=-1) return ret;
    ret = st!=0; // 不算状态0
    rep(i,26){
        if(trans[st][i]!=-1){
            ret += dfs(trans[st][i]);
        }
    }
    return ret;
}
void work(){
    idx=0;
    scanf("%s",s);
    n = strlen(s);
    ve.clear();
    rep(i,3)ve.PB(i);
    int sta = new_state(0,0,NULL,-1);
    do{
        deal(sta);
        sta = add_char(26 + 'a', sta);
    }while(next_permutation(ve.begin(), ve.end()));//全排列
    rep(i,idx) dp[i] = -1;
    int cnt = -1; // 因为状态0是空串不算进去
    for(int st = 0;st!=-1;st = trans[st][0], cnt++); // 计算同字符的串的数量(最大长度)
    printf("%lld\n", (dfs(0)+cnt*3)/6);
}
 
int main() {
    while(scd(n)==1)
        work();
}

J

第一次写树状数组,还有些地方不是很通透,比如神奇的lowbit。

先说一下最基础的树状数组,它就是维护区间和的,然后借助一些辅助数组,完成一些别的功能,比如,这一题就完成了区间不同元素的个数。

这题还用到一个倍增——将数列首尾相连,把两段连成一段

#include 
using namespace std;

const int N = 2e5 + 10;

int a[N];
int pre[N];
bool vis[N];
int last[N];
int nxt[N];
int bit[N];
int ans[N];

int lowbit(int x){
	return x & -x;
}
void update(int x, int y){
	for(int i = x; i < N; i += lowbit(i)){    //bit数组下标到不了N 
		bit[i] += y;
	}
}
int query(int x){
	int ans = 0;
	for(int i = x; i > 0; i -= lowbit(i)){    //bit数组是从2^0 ,即1开始计算的 
		ans += bit[i];
	}
	return ans;
}
int query(int x, int y){
	return query(y) - query(x - 1);
}

struct Seg{
	int l, r, id;
}seg[N];

bool cmp(Seg a, Seg b){
	return a. l < b. l;
}

int main(){
	int n, q;
	while(~scanf("%d%d", &n, &q)){
		memset(bit, 0, sizeof(bit));
		memset(vis, false, sizeof(vis));
		memset(nxt, -1, sizeof(pre));
		memset(last, -1, sizeof(last));
		for(int i = 1; i <= n; i ++){
			scanf("%d", &a[i]);
			a[i + n] = a[i];
		}
		n *= 2;
		pre[0] = 0;
		for(int i = 1; i <= n; i ++){
			if(! vis[a[i]]){
				vis[a[i]] = true;
				pre[i] = pre[i - 1] + 1;
			}
			else{
				pre[i] = pre[i - 1];
			}
			if(~last[a[i]]){
				nxt[last[a[i]]] = i;
			}
				last[a[i]] = i;
		}
		for(int i = 0; i < q; i ++){
			scanf("%d%d", &seg[i]. l, &seg[i]. r);
			seg[i]. l += n / 2;
			swap(seg[i]. l, seg[i]. r);
			seg[i]. id = i;
		}
		sort(seg, seg + q, cmp);
		
		int nowl = 1;
		for(int i = 0; i < q; i ++){
			while(nowl < seg[i]. l){
				if(~ nxt[nowl]){
					update(nxt[nowl], 1);
				}
				nowl ++;
			}
			ans[seg[i]. id] = pre[seg[i]. r] - pre[seg[i]. l - 1] + query(seg[i]. l, seg[i]. r);
		}
		for(int i = 0; i < q; i ++){
			printf("%d\n", ans[i]);
		}
	}
	return 0;
}

 

你可能感兴趣的:(2018牛客多校第一场)