多校9
A
贪心走最小的点,然后判环,如果走到一个环,就需要将与环有关的链全部废掉,因为这些节点一定有更优解
到这里可以\(O(n^3)\)水过
然后考虑有\(O(n^2)\)条路径,如何查询,如果对前驱建出一课路径树,询问本质上就是球一个点的dep = k的祖先。倍增即可做到\(O(n^2logn)\)
将询问离线下来,每次\(O(n)\)处理路径,\(O(nlogn)\)处理倍增,即可\(O(logn)\)处理每个询问
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N = 3000+10, E = (5000+10)*2, Q = 4e5+10;
int n, m , q;
struct graph{
vector<int> e[E];
bool inq[N];
void inse(int frm, int to){
e[frm].push_back(to);
}
int pre[N][N];
bool vis[N], inc[N];//inc:在环上
int top;//环顶部
bool ban[N];
void dfs(int s, int u){
inc[u] = true;
for(int v : e[u]){
if(inc[v]){
top = v;
break;
}else if(!ban[v]){
pre[s][v] = u;
dfs(s, v);
}
if(top || ban[u]){
break;
}
}
if(top || ban[u]){
fail(u);
}
if(u == top) top = 0;
inc[u] = false;
ban[u] = true;
}
void fail(int u){
if(ban[u]) return;
ban[u] = true;
for(int v : e[u]){
fail(v);
}
}
void get(int s){
top = 0;
memset(inc, 0, sizeof(inc));
memset(ban, 0, sizeof(ban));
dfs(s, s);
}
}G;
struct tree{
struct edge{
int nxt, to;
}e[E];
int head[N], elen;
void inse(int frm, int to){
e[++elen] = {head[frm], to};
head[frm] = elen;
}
static const int RG = 16;
int dep[N], fat[N][20];
void clear(){
memset(fat, 0, sizeof(fat));
memset(dep, 0, sizeof(dep));
memset(head, 0, sizeof(head));
elen = 0;
}
void dfs1(int u){
dep[u] = dep[fat[u][0]] + 1;
for(int i = head[u]; i;i = e[i].nxt){
int v = e[i].to;
if(v == fat[u][0]) continue;
fat[v][0] = u;
dfs1(v);
}
}
void init(){
for(int j = 1; j <=RG; ++j){
for(int i = 1; i <= n; ++i){
fat[i][j] = fat[fat[i][j-1]][j-1];
}
}
}
int get(int u, int x){//u的dep=x父亲
if(x > dep[u]) return -1;
int dt = dep[u] - x;
int ret = u;
for(int i = 0; i <= RG; ++i){
if(((1 <<(i))& dt)) ret = fat[ret][i];
}
return ret;
}
}T;
struct t_q{
int u, v, k, id;
};
vector<t_q> querys[N];
int ans[Q];
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> q;
for(int i = 1; i <= m;++i){
int u, v;
cin >> u >> v;
G.inse(u, v);
}
for(int i = 1; i <= n; ++i){
sort(G.e[i].begin(), G.e[i].end());
G.e[i].erase(unique(G.e[i].begin(), G.e[i].end()), G.e[i].end());
}
for(int i= 1; i <= q; ++i){
int u, v, k;
cin >> u >> v >> k;
querys[u].push_back({u, v, k, i});
}
for(int i = 1;i <= n; ++i){
if(!querys[i].size()) continue;
G.get(i);
T.clear();
for(int j = 1; j <= n; ++j){
if(G.pre[i][j]) T.inse(G.pre[i][j], j);
}
T.dfs1(i);
T.init();
for(t_q ask : querys[i]){
if(T.dep[ask.v]) {
ans[ask.id] = T.get(ask.v, ask.k);
}else{
ans[ask.id] = -1;
}
}
}
for(int i = 1; i <= q; ++i){
cout << ans[i] <<"\n";
}
return 0;
}
B
考场上SB了,这么SB的题直接跳了,5min拿20pts暴力分
一个数可以通过乘上他的最小奇质数因子,得到最小平方数,一个最小平方数可以乘上一个平方数得到一个平方数
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
bool npri[N];
int pri[N];
int num[N];
int n, m;
void sieve(int len){
for(int i = 2; i <= len; ++i){
if(!npri[i]) pri[++pri[0]] = i, num[i] = i;
for(int j = 1; j <= pri[0] && pri[j] * i <= len; ++j){
npri[i * pri[j]] = true;
if(i % pri[j] == 0){
num[i * pri[j]] = num[i];
break;
}
num[i * pri[j]] = pri[j];
}
}
}
int d[N];
int sqr[N];
void split(int x){
int rw = x;
d[x] = 1;
while(x != 1){
int cnt = 0;
int div = num[x];
while(x % div == 0) x /= div, ++cnt;
if(cnt & 1) d[rw] = d[rw] * div;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
sieve(2e6+10);;
cin >> n >> m;
for(int i = 1; i <= n; ++i){
split(i);
}
int bound = ceil(sqrt(m));
for(int i = 1; i <= bound; ++i){
sqr[i] = i * i;
}
int ans = 0;
for(int i = 1; i <= n; ++i){
int lim = floor(1.0*m/d[i]);
int pos = upper_bound(sqr + 1, sqr + 1 + bound, lim) - sqr-1;
ans = ans + pos;
}
cout << ans << endl;
return 0;
}
C
略(不想写正解,吉司机yyds!)
D
第一眼非常小清新的数位dp
然而需要高精度
学习了一种极限卡常方式
用unsigned long long 压17位,用const取模(编译器自动优化)
减少乘法,用加法替换乘法
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 500+10, K = 15;
typedef unsigned long long ull;
struct big{
static const int L = 60, BIT = 17;
static const ull MOD = 1E17;
ull a[L];
big(){
clear();
}
void clear(){
memset(a, 0, sizeof(a));
}
void set0(){
for(int i = 1; i <= a[0]; ++i) a[i] = 0;
}
bool is0(){
return a[0] == 1 && a[1] == 0;
}
void init(const char str[]){
int len = strlen(str + 1);
a[0] = ceil(1.0 * len /BIT);
for(int i = 1; i <= len; ++i){
int j= ceil(1.0 * (len - i + 1)/ BIT);
a[j] = a[j] * 10 + str[i] - '0';
}
}
big(int x){
clear();
init(x);
}
big(const char str[]){
clear();
init(str);
}
void init(int x){
a[1] = x;
a[0] = 1;
}
friend big operator +(const big&a ,const big& b){
big c;
c.a[0] = max(a.a[0], b.a[0]);
for(int i = 1; i <= c.a[0]; ++i){
c.a[i] = a.a[i] + b.a[i];
}
for(int i = 1; i <= c.a[0]; ++i){
c.a[i+1]+=c.a[i]/MOD,c.a[i]%=MOD;
}
if(c.a[c.a[0] + 1]) ++c.a[0];
return c;
}
big operator +=(const big& b){
a[0] = max(a[0], b.a[0]);
for(int i = 1; i <= a[0]; ++i){
a[i] = a[i] + b.a[i];
}
for(int i = 1; i <= a[0]; ++i){
a[i+1]+=a[i]/MOD,a[i]%=MOD;
}
if(a[a[0] + 1]) ++a[0];
return *this;
}
friend big operator *(const big& a, const int b){
big c;
c.a[0] = a.a[0];
for(int i = 1; i <= a.a[0]; ++i){
c.a[i] = a.a[i] * b;
}
for(int i = 1; i <= c.a[0]; ++i){
c.a[i + 1] += c.a[i] / MOD, c.a[i] %= MOD;
}
if(c.a[c.a[0] + 1]) ++c.a[0];
while(c.a[0] > 1 && !c.a[c.a[0]]) --c.a[0];
return c;
}
ull operator[](int x)const{
return a[x];
}
ull& operator [](int x){
return a[x];
}
void print(){
printf("%llu", a[a[0]]);
for(int i = a[0] - 1; i >= 1; --i){
printf("%0*llu",BIT, a[i]);
}
}
};
big dp1[2][(1 << 10) + 10];
big dp2[2][(1 << 10) + 10];
big pw10, pwx;
int ban[K];
int nxt[(1 << 10) + 10];//状态的不可行状态
int n,m,k;
int main(){
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= m; ++i){
int u, v;
scanf("%d%d", &u, &v);
ban[u] |= 1 <<(v-1);//能否在后面添加
}
for(int i = 0; i < (1 << k); ++i){
for(int j = 1; j <= k; ++j){
if(i & (1 << (j-1))){
nxt[i] |= ban[j];//i状态不能根j
}
}
}
dp1[0][0] = 1, dp2[0][0] = 0;
for(int i = 0; i <= n; ++i){
int d = i & 1;
for(int j = 0; j < (1 << k); ++j)
dp1[!d][j].set0(), dp2[!d][j].set0();
for(int j = 0; j <(1 << k); ++j){
if(dp1[d][j].is0()) continue;
pw10 = dp2[d][j] *10, pwx = 0;
for(int x = 1; x <= k; ++x){
pwx += dp1[d][j];
if(nxt[j] & (1 << (x-1))) continue;
dp1[!d][j | (1 << (x-1))] += dp1[d][j];
dp2[!d][j | (1 << (x-1))] += pw10 + pwx;
}
}
}
big ans1 = 0, ans2 = 0;
for(int i = 0; i < (1 << k); ++i){
ans1 += dp1[n & 1][i];
ans2 += dp2[n & 1][i];
}
ans1.print();
printf("\n");
ans2.print();
printf("\n");
return 0;
}