Codeforces Round# 305 (Div 1)

[Codeforces 547A]

#include <bits/stdc++.h>

#define maxn 1000010
using namespace std;
typedef long long ll;
bool vis[maxn];
int md, h, a, x, y;

int Go(int h, int x, int y){
	memset(vis, 0, sizeof vis);
	int cnt = 0;
	while(true){
		if(vis[h])
			return -1;
		vis[h] = 1;
		h = (1ll * h * x + y) % md;
		++ cnt;
		if(h == a)
			return cnt;
	}
}

int main(){
	scanf("%d", &md);
	scanf("%d%d%d%d", &h, &a, &x, &y);
	int a1 = Go(h, x, y), b1 = Go(a, x, y);
    scanf("%d%d%d%d", &h, &a, &x, &y);
    int a2 = Go(h, x, y), b2 = Go(a, x, y);
    if(a1 == -1 || a2 == -1)return puts("-1"), 0;
	if(b1 == -1 && b2 == -1){
		if(a1 == a2)printf("%d\n", a1);
		else printf("-1\n");
		return 0;
	}
	if(b1 != -1 && b2 != -1){
		for(int i = 0; i <= md; i ++){
			if(a1 + 1ll * b1 * i >= a2 && (a1 + 1ll * b1 * i - a2) % b2 == 0){
				cout << a1 + 1ll * b1 * i << endl;
				return 0;
			}
		}return puts("-1"), 0;
	}
	else{
		if(b1 == -1)swap(a1, a2), swap(b1, b2);
		if(a2 >= a1 && (a2 - a1) % b1 == 0)
		    printf("%d\n", a2);
		else return puts("-1"), 0;
	}
	return 0;
}

  

[Codeforces 547B]

题目大意: 就是给你一个序列,定义f(x)为(所有区间长度为x的最小值)的最大值,输出f(1)....f(n)

n ≤ 2 * 10^5

 

Sol:感觉用并查集就可以水。。

#include <bits/stdc++.h>
#define maxn 200010
using namespace std;

int n;
int a[maxn];

int fa[maxn];
int getfa(int x){return x == fa[x] ? x : fa[x] = getfa(fa[x]);}

int h[maxn];

bool cmp(int i, int j){
	if(a[i] == a[j]) return i < j;
	return a[i] < a[j];
}

int t;

int size[maxn], f[maxn];
inline void update(int x){
	fa[x] = x, size[x] = 1;
	if(t = getfa(x-1))fa[t] = x, size[x] += size[t];
	if(t = getfa(x+1))fa[t] = x, size[x] += size[t];
	f[size[x]] = max(f[size[x]], a[x]);
	return;
}

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++)
		scanf("%d", &a[i]), h[i] = i;
	sort(h+1, h+1+n, cmp);

	for(int i = n; i >= 1; i --)
		update(h[i]);
	for(int i = n; i >= 1; i --)
	    f[i] = max(f[i+1], f[i]);
	    
	for(int i = 1; i <= n; i ++)
	    printf("%d ", f[i]);
	return 0;
}

[Codeforces 547C]

题目大意:给定一个序列,初始为空,每次从序列中选出一个数字添加或删除,求gcd(Ai, Aj) = 1的有序数对(i, j)的个数。

大爷题解传送门

容斥原理。

#include <bits/stdc++.h>
#define maxn 1000010
using namespace std;

int N, Q;

int A[maxn];

vector<int> P[maxn];

bool vis[maxn];
int p[maxn], primes, mn[maxn];

void prepare(){
	mn[1] = 1;
	int N = 500000;
	for(int i = 2; i <= N; i ++){
		if(!vis[i]){
			p[primes ++] = i;
			mn[i] = i;
		}
		for(int j = 0; j < primes; j ++){
			if(1ll * i * p[j] > N)break;
			vis[i * p[j]] = true;
			if(i % p[j] == 0){mn[i * p[j]] = p[j];break;}
			mn[i * p[j]] = mn[i];
		}
	}
}

void FJ(int Id, int A){
	while(A != 1){
		int now = mn[A];
		while(A % now == 0)
		    A /= now;
		P[Id].push_back(now);
	}
}

long long ans;

int Bits[100], cnt[maxn];

void update(int Id, int val){
	int n = P[Id].size(), S = (1 << n) - 1;
	for(int i = 0; i <= S; i ++){
        long long LCM = 1;
		for(int j = 0; j < n; j ++)
			if(i >> j & 1)
				LCM = LCM * P[Id][j];
		cnt[LCM] += val;
	}
}

int QAQ(int S, int now){
    int n = P[now].size();
    long long ret = 0, LCM = 1;
	for(int i = 0; i < n; i ++)
		if(S >> i & 1)
			LCM = LCM * P[now][i];
	return cnt[LCM];
}

long long work(int now){
	int n = P[now].size();
	int S = (1 << n) - 1;
	long long ret = 0;
	for(int i = 0; i <= S; i ++)
        if(Bits[i] & 1)ret -= QAQ(i, now);
        else ret += QAQ(i, now);
	return ret;
}

void solve(int Id){
	if(vis[Id])update(Id, -1), ans -= work(Id);
	else ans += work(Id), update(Id, 1);
	vis[Id] ^= 1;
}

int main(){
	prepare();
	for(int i = 1; i < 1<<6; i ++)
	    Bits[i] = __builtin_popcount(i);
	scanf("%d%d", &N, &Q);
	for(int i = 1; i <= N; i ++)
	    scanf("%d", &A[i]);
	for(int i = 1; i <= N; i ++)
		FJ(i, A[i]);
	memset(vis, 0, sizeof vis);
	int Id;
	for(int i = 1; i <= Q; i ++){
		scanf("%d", &Id);
		solve(Id);
		printf("%I64d\n", ans);
	}
	return 0;
}

  

[Codeforces 547D]

题目大意:平面上有n个点,你需要给他们一一染色,可以染'b'(blue)或'r'(red),要求每一行和每一列所染色的(红色-蓝色)数目的绝对值<=1,输出染色方案。

n ≤ 2 * 10^5

 

 

QAQ虽然说一看就知道是网络流但是数据范围也是吓我一跳QAQ

然而YY半天建图无果。。

首先行和列示要看成点的,给的每一个点(x, y)看成一条边无疑,容量为1

然后广告犇提出了限流思想:既然每行或每列不能相差超过1,那么就可以从源点->每一行连接容量为(cntx[i] / 2)的边,这样红蓝就一定符合条件了!(其中cntx[i]表示第i行有多少个点),从每一列连向汇点同理QAQ

但是这样输出的方案可能还不够,有一些点仍然没有确定怎么办?(假如某一行某一列的点的个数为奇数。。)

然后就弃疗了

orz了一下神犇的题解(虽然说并不知道神犇是谁,但是网络流竟然可以过QAQ)

先把行列离散化一下QAQ,然后如上建边,然后跑一边最大流,然后如果有多出来的就再添加一条容量为1的边,再跑一遍最大流。最后如果割掉了(edge[i<<1])说明此点和S相连,否则和T相连。

某犇:我快YY出正解了

主要程序:(网络流就不写了QAQ)

int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++)
	    scanf("%d%d", &x[i], &y[i]);
    for(int i = 1; i <= n; i ++)
        ++ cx[x[i]], ++ cy[y[i]];
	int id = 0;
	for(int i = 1; i <= 200000; i ++)
		if(cx[i])Idx[i] = ++ id;
    for(int i = 1; i <= 200000; i ++)
		if(cy[i])Idy[i] = ++ id;
	T = id + 1;
	for(int i = 1; i <= n; i ++)
	    add(Idx[x[i]], Idy[y[i]], 1);
	for(int i = 1; i <= 200000; i ++)
		if(cx[i] > 1) add(S, Idx[i], cx[i] >> 1);
    for(int i = 1; i <= 200000; i ++)
		if(cy[i] > 1) add(Idy[i], T, cy[i] >> 1);
	
	Dinic();
	for(int i = 1; i <= 200000; i ++)
	    if(cx[i] & 1) add(S, Idx[i], 1);
    for(int i = 1; i <= 200000; i ++)
	    if(cy[i] & 1) add(Idy[i], T, 1);
	Dinic();
	for(int i = 1; i <= n; i ++)
	    if(!edge[i<<1].w)
			putchar('b');
		else putchar('r');
	puts("");
	return 0;
}

[Codeforces 547E]

大爷题解传送门

给出一大堆字符串,每次询问第k个串在第l~r的串中作为子串出现了多少次

简述:AC自动机(failtree)的dfs序做可持久化。

#include <bits/stdc++.h>
#define maxn 200010
using namespace std;

int n, m;

char str[maxn];

int tr[maxn][26], root, smz;

int pos[maxn];

struct Edge{
	int to, nxt;
}edge[maxn];
int h[maxn], cnt;
void add(int u, int v){
	cnt ++;
	edge[cnt].to = v;
	edge[cnt].nxt = h[u];
	h[u] = cnt;
}


void init(){
	memset(tr, -1, sizeof tr);
	root = smz = 0;
}

void Insert(int Id){
	int N = strlen(str+1), now = root;
	for(int i = 1; i <= N; i ++){
		int c = str[i] - 'a';
		if(tr[now][c] == -1)tr[now][c] = ++ smz;
		now = tr[now][c];
	}
	pos[Id] = now;
}

queue<int> Q;

int fa[maxn], fail[maxn];
void buildfail(){
	for(int i = 0; i < 26; i ++)
	    if(tr[root][i] == -1) tr[root][i] = root;
	    else fail[tr[root][i]] = root, Q.push(tr[root][i]), fa[tr[root][i]] = root;
	while(!Q.empty()){
		int u = Q.front(); Q.pop();
		for(int i = 0; i < 26; i ++){
			if(tr[u][i] == -1)tr[u][i] = tr[fail[u]][i];
			else fail[tr[u][i]] = tr[fail[u]][i], Q.push(tr[u][i]), fa[tr[u][i]] = u;
		}
	}
	for(int i = 1; i <= smz; i ++)
	    add(fail[i], i);
}

int In[maxn], Out[maxn], dfs_clock;

void dfs(int u){
	In[u] = ++ dfs_clock;
	for(int i = h[u]; i; i = edge[i].nxt)
		dfs(edge[i].to);
	Out[u] = dfs_clock;
}

#define M 10000010
int T[maxn], L[M], R[M], val[M];

int size;

int build(int l, int r){
	if(l > r) return 0;
	int now = ++ size;
	int mid = l + r >> 1;
	L[now] = build(l, mid - 1);
	R[now] = build(mid + 1, r);
	return now;
}

int update(int rt, int pos, int l, int r){
	int mid = l + r >> 1, now = ++ size;
	L[now] = L[rt], R[now] = R[rt], val[now] = val[rt] + 1;
	if(l == r) return now;
	if(pos <= mid)L[now] = update(L[rt], pos, l, mid);
	else R[now] = update(R[rt], pos, mid + 1, r);
	return now;
}

int ask(int rt, int al, int ar, int l, int r){
	if(al == l && ar == r)return val[rt];
	int mid = l + r >> 1;
	if(ar <= mid)return ask(L[rt], al, ar, l, mid);
	if(al > mid) return ask(R[rt], al, ar, mid + 1, r);
	return ask(L[rt], al, mid, l, mid) + ask(R[rt], mid + 1, ar, mid + 1, r);
}

int main(){
	init();
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++)
		scanf("%s", str+1), Insert(i);
	buildfail();
	dfs(root);
	T[0] = build(1, dfs_clock);
	for(int i = 1; i <= n; i ++){
		T[i] = T[i-1];
		for(int j = pos[i]; j; j = fa[j])
			T[i] = update(T[i], In[j], 1, dfs_clock);
	}

	int l, r, k;
	for(int i = 1; i <= m; i ++){
		scanf("%d%d%d", &l, &r, &k); l --;
		printf("%d\n", ask(T[r], In[pos[k]], Out[pos[k]], 1, dfs_clock) - ask(T[l], In[pos[k]], Out[pos[k]], 1, dfs_clock));
	}
	return 0;
}


/*
5 5
a
ab
abab
ababab
b
1 5 1
3 5 1
1 5 2
1 5 3
1 4 5
*/

  

转载于:https://www.cnblogs.com/Candyouth/p/5349825.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值