noip模拟4

赛时rank 6,T1 20,T2 55,T3 100,T4 0

悔了,我赛时应该先开T3T4的。看了两个小时的T1不会做,T4一眼切了没时间打了

\(Cai\)

由于一些不可抗力因素,直接挂原题链接了

T1 最短路

[USACO09DEC] Cow Toll Paths G

\(solution:\)

我们发现\(n\)很小,考虑\(floyd\)

正常的\(floyd\)可以解决边权之和最小值,那么如何让其顺带着解决点权最大值捏?

将点按照点权从小到大排序就可以啦

\(dist_{i,j}=\min\{dist_{i,j},dist_{i,k}+dist_{k,j}\}\)
这个是\(floyd\)原数组,但是因为点按照点权排过序,所以转化一下即可。

\(ans_{i,j}=\min\{ans_{i,j},dist{i,j}+\max\{val(i),val(j),val(k)\}\}\)
这是统计答案的数组,记得转化

\(code:\)

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
	int value,dis;
	friend bool operator < (const node &x,const node &y){
		return x.value<y.value;
	}
}a[999999];
int edge[400][400];
int n,m,q;
int ans[400][400];
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
#ifdef LOCAL
    FILE *InFile=infile("in.in"),*OutFile=outfile("out.out");
#else
    FILE *InFile=infile("path.in"),*OutFile=outfile("path.out");
#endif
signed main(){
	memset(edge,0x3f,sizeof edge);
	memset(ans,0x3f,sizeof ans);
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m>>q;
	for(int i=1;i<=n;++i){
		cin>>a[i].value;
		a[i].dis=i;
		edge[i][i]=0;
	}
	for(int i=1;i<=m;++i){
		int x,y,z;
		cin>>x>>y>>z;
		edge[x][y]=edge[y][x]=min(edge[x][y],z);
	}
	sort(a+1,a+1+n);
	for(int k=1;k<=n;++k){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				edge[a[i].dis][a[j].dis]=min(edge[a[i].dis][a[j].dis],edge[a[i].dis][a[k].dis]+edge[a[k].dis][a[j].dis]);
				ans[a[i].dis][a[j].dis]=min(ans[a[i].dis][a[j].dis],edge[a[i].dis][a[j].dis]+max({a[i].value,a[j].value,a[k].value}));
			}
		}
	}
	while(q--){
		int x,y;
		cin>>x>>y;
		cout<<(ans[x][y]==ans[0][0]?-1:ans[x][y])<<'\n';
	}
}

T2 方格取数

[POI2008] KUP-Plot purchase

\(solution:\)

有一个二分套二分加卡常的\(O(n^2\log^2n)\)做法,没有好好处理,瞎写的,但是因为学校数据水,于是就过了,在luogu上没有,挂一下吧。看代码很好理解的。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
#ifdef LOCAL
    FILE *InFile=infile("in.in"),*OutFile=outfile("out.out");
    // FILE ErrFile=errfile("err.err");
#else
    // FILE *InFile=infile("matrix.in"),*OutFile=outfile("matrix.out");
#endif
namespace IO {
#ifdef LOCAL
FILE *Fin(fopen("in.in", "r")), *Fout(fopen("out.out", "w"));
#else
FILE *Fin(fopen("matrix.in", "r")), *Fout(fopen("matrix.out", "w"));
#endif
class qistream {
    static const size_t SIZE = 1 << 24, BLOCK = 64;
    FILE *fp;
    char buf[SIZE];
    int p;
public:
    qistream(FILE *_fp = stdin): fp(_fp), p(0) {
        fread(buf + p, 1, SIZE - p, fp);
    } void flush() {
        memmove(buf, buf + p, SIZE - p), fread(buf + SIZE - p, 1, p, fp), p = 0;
    } qistream &operator>>(char &str) {
        str = getch();

        while (isspace(str))
            str = getch();

        return*this;
    } template<class T>qistream &operator>>(T &x) {
        x = 0;
        p + BLOCK >= SIZE ? flush() : void();
        bool flag = false;

        for (; !isdigit(buf[p]); ++p)
            flag = buf[p] == '-';

        for (; isdigit(buf[p]); ++p)
            x = x * 10 + buf[p] - '0';

        x = flag ? -x : x;
        return*this;
    } char getch() {
        return buf[p++];
    } qistream &operator>>(char *str) {
        char ch = getch();

        while (ch <= ' ')
            ch = getch();
        int i;
        for (i = 0; ch > ' '; ++i, ch = getch())
            str[i] = ch;
        str[i] = '\0';

        return*this;
    }
} qcin(Fin);
class qostream {
    static const size_t SIZE = 1 << 24, BLOCK = 64;
    FILE *fp;
    char buf[SIZE];
    int p;
public:
    qostream(FILE *_fp = stdout): fp(_fp), p(0) {}~qostream() {
        fwrite(buf, 1, p, fp);
    } void flush() {
        fwrite(buf, 1, p, fp), p = 0;
    } template<class T>qostream &operator<<(T x) {
        int len = 0;
        p + BLOCK >= SIZE ? flush() : void();
        x < 0 ? (x = -x, buf[p++] = '-') : 0;

        do
            buf[p + len] = x % 10 + '0', x /= 10, ++len;

        while (x)
            ;

        for (int i = 0, j = len - 1; i < j; ++i, --j)
            swap(buf[p + i], buf[p + j]);

        p += len;
        return*this;
    } qostream &operator<<(char x) {
        putch(x);
        return*this;
    } void putch(char ch) {
        p + BLOCK >= SIZE ? flush() : void();
        buf[p++] = ch;
    } qostream &operator<<(char *str) {
        for (int i = 0; str[i]; ++i)
            putch(str[i]);

        return*this;
    }
} qcout(Fout);
} using namespace IO;
#define cin qcin
#define cout qcout
const int N = 2010;
ll sum[N][N],k;
int n;
inline ll get(int i,int j,int i1,int j1){
    return sum[i][j]-sum[i1-1][j]-sum[i][j1-1]+sum[i1-1][j1-1];
}
signed main(){
    //cin.tie(nullptr)->sync_with_stdio(false);
    //cout.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>k;
    for(int i = 1;i <= n; ++i)
        for(int j = 1;j <= n; ++j)  
            cin>>sum[i][j];
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= n; ++j)
            sum[i][j] = sum[i][j]-sum[i-1][j-1]+sum[i-1][j]+sum[i][j-1];
    }
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= n; ++j){
            int L = 1,R = i;
            while(L<=R){
                int i1 = (L+R)>>1;
                int l = 1,r = j;
                ll mx = 0,mn = LLONG_MAX;
                while(l<=r){
                    int j1 = (l+r)>>1;
                    ll emm = get(i,j,i1,j1);
                    mx = max(mx,emm);
                    mn = min(mn,emm);
                    if(emm > k*2) {l = j1+1;continue;}
                    if(emm < k) {r = j1-1;continue;}
                    cout<<i1<<' '<<j1<<' '<<i<<' '<<j<<'\n';
                    return 0;
                }
                if(mx > k*2) L = i1+1;
                if(mn < k) R = i1-1;
            }
        }
    }
    cout<<-1;
}

有些人不会写二维前缀和现推的还推错了,白搭10min

正解是\(O(n^2)\)的悬线法

考虑\(>2*k\)的肯定不选,在\([k,2*k]\)的就是答案,所以就转化为了求\(<k\)的元素的一个矩阵,使得其的和在区间中。

如果最大的子矩阵的和\(sum\ge k\),那么就一定可以找到一个最大子矩阵的子矩阵满足条件。

取出其第一行的和\(sum_1\)

如果\(sum_1\ge k\),答案就在第一行,一个一个删即可。

反之,直接舍弃。

\(code:\)

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
#ifdef LOCAL
    FILE *InFile=infile("in.in"),*OutFile=outfile("out.out");
    // FILE ErrFile=errfile("err.err");
#else
#endif
const int N = 2010;
int n,l[N][N],r[N][N],h[N][N];
ll k,sum[N][N],val[N][N];
inline ll get(int a,int b,int c,int d){
    return sum[c][d] - sum[a - 1][d] - sum[c][b - 1] + sum[a - 1][b - 1];
}
inline void Work(int a,int b,int c,int d){
    while(get(a,b,c,d) > 2*k){
        if(a == c) d--;
        else if(get(a,b,c,d) >= k) c = a;
        else a++;
    }
    cout<<b<<' '<<a<<' '<<d<<' '<<c<<'\n';
    exit(0);
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    cin>>k>>n;
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= n; ++j){
            cin>>sum[i][j];
            val[i][j] = sum[i][j];
            if(k <= val[i][j] && val[i][j] <= 2*k)
                return cout<<j<<' '<<i<<' '<<j<<' '<<i<<'\n',0;
        }
    }
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= n; ++j){
            sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+val[i][j];
        }
    }
    for(int i = 1;i <= n; ++i) r[0][i] = n+1;
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= n; ++j){
            if(val[i][j] <= 2*k) h[i][j] = h[i-1][j]+1;
        }
    }
    for(int i = 1;i <= n; ++i){
        int res = 0;
        for(int j = 1;j <= n; ++j){
            if(h[i][j]) l[i][j] = max(l[i-1][j],res);
            else res = j,l[i][j] = 0;
        }
        res = n + 1;
        for(int j = n; j; --j){
            if(h[i][j]) r[i][j] = min(r[i-1][j],res);
            else res = j,r[i][j] = n + 1;
        }
    }
    for(int i = 1;i <= n; ++i){
        for(int j = 1;j <= n; ++j){
            if(!h[i][j]) continue;
            int a = i - h[i][j] + 1;
            int b = l[i][j] + 1;
            int d = r[i][j] - 1 ;
            if(get(a,b,i,d) < k) continue;
            Work(a,b,i,d);
        }
    }
    cout<<"NIE";
}

T3 数组

\(solution:\)

Please, another Queries on Array?

大水题。

考虑

\[\varphi(n)=n\prod_{p\in Prime\&p|n}\frac{p-1}{p} \]

\[\varphi(kn)=kn\prod_{p\in Prime\&p|kn}\frac{p-1}{p} \]

就是考虑有几个素因子呗,考虑到插入的数很小,将300以内的素数预处理出来,大概是有62个,用一个bitset判断是否出现过,线段树维护区间乘积,区间的bitset,由于乘积会很大,需要取模,就要预处理逆元。

具体看代码吧,比较好理解。

\(code:\)

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
#ifdef LOCAL
    FILE *InFile=infile("in.in"),*OutFile=outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *InFile=infile("array.in"),*OutFile=outfile("array.out");
#endif
// #define int long long
const int N = 1e5 + 10,mod = 1e9 + 7;
int pos[300];
vector<int> prime={2,3,5,7,11,13,17,19,
23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,
101,103,107,109,113,127,131,137,139,149,151,
157,163,167,173,179,181,191,193,197,199,211,223,227,
229,233,239,241,251,257,263,269,271,277,281,283,293};
inline int power(int a,int b,int mod){
    int res = 1;
    while(b){
        if(b&1) res = 1ll*res*a%mod;
        b>>=1;a = 1ll*a*a%mod;
    }
    return res;
}
int n,a[N],q,inv[N];
bitset<65> pre[310];
struct Segment_Tree{
private:
    struct segment_tree{
        int l,r;
        ll val,lazy1 = 1;
        bitset<65> pd_prime,lazy2;
        #define l(x) tree[x].l
        #define r(x) tree[x].r
        #define val(x) tree[x].val
        #define lazy1(x) tree[x].lazy1
        #define lazy2(x) tree[x].lazy2
        #define pd(x) tree[x].pd_prime
    }tree[N<<2];
    inline void pushup(int k){
        val(k) = val(k<<1)*val(k<<1|1)%mod;
        tree[k].pd_prime=(tree[k<<1].pd_prime|tree[k<<1|1].pd_prime);
    }
    inline void pushdown(int k){
        if(lazy1(k)!=1){
            int ls =k<<1,rs = k<<1|1;
            val(ls) = 1ll*val(ls)*power(lazy1(k),r(ls)-l(ls)+1,mod)%mod;
            lazy1(ls) = lazy1(ls)*lazy1(k)%mod;
            lazy2(ls) |= lazy2(k);
            pd(ls)|=lazy2(k);
            val(rs) = 1ll*val(rs)*power(lazy1(k),r(rs)-l(rs)+1,mod)%mod;
            pd(rs)|=lazy2(k);
            lazy1(rs) = lazy1(rs)*lazy1(k)%mod;
            lazy2(rs) |= lazy2(k);
            lazy2(k).reset();
            lazy1(k)=1;
        }
    }
public:
    void build(int k,int l,int r){
        l(k) = l,r(k) = r;
        if(l == r){
            val(k) = a[l];
            tree[k].pd_prime = pre[val(k)];
            return;
        }
        int mid = (l+r)>>1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        pushup(k);
    }
    void update(int k,int l,int r,int val,bitset<65> prim){
        if(l <= l(k) && r(k) <= r){
            val(k) = 1ll*val(k)*power(val,r(k)-l(k)+1,mod)%mod;
            pd(k)|=prim;
            lazy1(k) = 1ll*lazy1(k)*val%mod;
            lazy2(k) |= prim;
            return;
        }
        pushdown(k);
        int mid = (l(k)+r(k))>>1;
        if(l <= mid) update(k<<1,l,r,val,prim);
        if(r > mid) update(k<<1|1,l,r,val,prim);
        pushup(k);
    }
    pair<ll,bitset<65> > query(int k,int l,int r){
        if(l <= l(k) && r(k) <= r) return make_pair(val(k),pd(k));
        pushdown(k);
        int mid = (l(k)+r(k))>>1;
        pair<ll,bitset<65> > res;res.first = 1;
        if(l <= mid){
            pair<ll,bitset<65> > ll = query(k<<1,l,r);
            res.first = res.first*ll.first%mod;
            res.second|=ll.second;
        }
        if(r > mid){
            pair<ll,bitset<65> > ll = query(k<<1|1,l,r);
            res.first = res.first*ll.first%mod;
            res.second|=ll.second;
        }
        return res;
    }
}T;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    for(int i = 0;i < prime.size(); ++i) pos[prime[i]] = i;
    for(auto i:prime) inv[i] = power(i,mod-2,mod);
    for(int x = 1;x <= 300; ++x){
        for(auto i : prime){
            if(i > x) break;
            if(x % i == 0) pre[x][pos[i]] = true;
        }
    }
    cin>>n>>q;
    for(int i = 1;i <= n; ++i) cin>>a[i];
    T.build(1,1,n);
    int op,l,r,x;
    while(q--){
        cin>>op>>l>>r;
        if(op == 1){
            cin>>x;
            T.update(1,l,r,x,pre[x]);
        }
        else{
            pair<ll,bitset<65> > res = T.query(1,l,r);
            ll ans = res.first;
            for(auto i:prime){
                if(res.second[pos[i]]){
                    ans = 1ll*ans*(i-1)%mod*inv[i]%mod;
                }
            }
            cout<<ans<<'\n';
        }
    }
}

T4 树

\(solution:\)

一眼根号分治,赛场没时间打了。现在也没时间。我要去水,晚上在写。

update:
总共有两种做法

  1. 求k级祖先,根号分治预处理,比较板子(好像可以用长链剖分\(O(1)\)求k级祖先,复杂度较优)
  2. 利用树剖的特性,将树排成dfs序,在序列上分块进行维护

等放假了再打吧,先去学四边形不等式了,逃

posted @ 2024-07-12 17:33  CuFeO4  阅读(6)  评论(0编辑  收藏  举报