【Author : DS】(CUGB 120918 浙大2012 - 08月赛) 是一套好题


前言:

长春比赛没几天了,熟悉浙大的出题风格,就要从AK几乎浙大月赛开始。加油!


A

说实话只是知道方向,具体的想不清楚。老婆喊了一句——从右往左第一个重复的,于是就直接想到线段树解法了。

离线操作——按照尾巴排序,更新中间重复的段落。

存疑 :如果是从左往右,咋办。。。

#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define LL int
const int maxn = 600000;
LL add[maxn<<2];
LL sum[maxn<<2];
void PushUp(int rt) {
	sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void PushDown(int rt,int m) {
	if (add[rt]) {
		add[rt<<1] = add[rt];
		add[rt<<1|1] = add[rt];
		sum[rt<<1] = add[rt];
		sum[rt<<1|1] = add[rt];
		add[rt] = 0;
	}
}
void build(int l,int r,int rt) {
	add[rt] = 0;
	if (l == r) {
		//scanf("%lld",&sum[rt]);
		sum[rt] = 0;
		return ;
	}
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
	PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt) {
	if (L <= l && r <= R) {
		add[rt] = c;
		sum[rt] = c;
		return ;
	}
	PushDown(rt , r - l + 1);
	int m = (l + r) >> 1;
	if (L <= m) update(L , R , c , lson);
	if (m < R) update(L , R , c , rson);
	PushUp(rt);
}
LL query(int p,int l,int r,int rt) {
	if (l == r) {
		return sum[rt];
	}
	PushDown(rt , r - l + 1);
	int m = (l + r) >> 1;
	if (p <= m) return query(p , lson);
	else return query(p , rson);
}
map<int , int> Meng;
map<int , int> ::iterator iter;
const int N = 1000000;
int nex[N];
int n , a[N] , Q;
int ans[N];
struct ZHU{
    int l , r , id;
    bool operator < (const ZHU & A) const{
        return r < A.r;
    }
    void input(){
        scanf("%d%d", &l , &r);
    }
}io[N];
void solve(){
    Meng.clear();
    memset(nex , -1 , sizeof(nex));
    for (int i = 1 ; i <= n ; ++i){
        scanf("%d" , &a[i]);

    }
    for (int i = n ; i >= 1 ; --i){
        iter = Meng.find(a[i]);
        if (iter != Meng.end()){
            nex[i] = iter->second;
        }
        Meng[a[i]] = i;
    }
    build(1 , n , 1);
    memset(ans , 0 , sizeof(ans));
    scanf("%d", &Q);
    for (int i = 1 ; i <= Q ; ++i){
        io[i].input();
        io[i].id = i;
    }
    sort(io + 1 , io + 1 + Q);
    int j = 1;
    for (int i = 1 ; i <= Q; ++i){
        while (j <= io[i].r){
            if (nex[j] == -1) {
                ++j;
                continue;
            }
            //printf("Update %d: %d\n",i,pre[j]);
            update(nex[j] , n , j , 1 , n , 1);
            j++;
        }
        int res = query(io[i].r , 1 , n , 1);
        if (res >= io[i].l) ans[io[i].id] = res;
        else ans[io[i].id] = 0;
    }
    for (int i = 1 ; i <= Q ; ++i){
        if (ans[i] == 0) printf("OK\n");
        else printf("%d\n",a[ans[i]]);
    }
    printf("\n");

}
int main(){
    while (cin >> n) solve();
}



B

貌似就是一个很水的贪心,Bella 648B 就过了。表示困了没法看。。。



C

记得是树状数组+二分,NlogNlogN,吴宏给过NlogN的算法,一直没来得及看。。。罪过罪过。

int n , m ;
const int N = 60000;
int a[N] , b , tree[N];
void add(int p){
    for (int i = p ; i <= n ; i += low_bit(i))
        tree[i] += 1;
}
int getsum(int p){
    int ret = 0;
    for (int i = p ; i >= 1 ; i-= low_bit(i))
        ret += tree[i];
    return ret;
}
int ans[N];
void solve(){
    memset(tree , 0 , sizeof(tree));
    for (int i = 1 ; i <= n ; ++ i){
        scanf("%d", &a[i]);
        b = a[i];
        int low = 1, high = n , ret = n , mid;
        do{
            int mid = (low + high) >> 1;
            if (mid - getsum(mid) >= b){
                ret = min(ret , mid);
                high = mid - 1;
            }
            else low = mid + 1;
        }while (low <= high);
        ans[i] = ret;
        add(ret);
    }
    scanf("%d",&m);
    for (int i = 1 ; i <= m ; ++i){
        scanf("%d",&b);
        if (i > 1) printf(" ");
        printf("%d",ans[b]);
    }
    printf("\n");
}
int main(){
    while (cin >> n) solve();
}

吴宏的NlogN线段树:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;

int a[60000];

struct node{
    int l,r,num;
}tree[300000];
int ans,now;
int n,m,man;

void build(int i,int left,int right)
{
    node &p=tree[i];
    p.l=left;
    p.r=right;
    if (left==right)
    {
        p.num=1;
        return;
    }
    int mid=(left+right)>>1;
    build(i<<1,left,mid);
    build(i<<1|1,mid+1,right);
    p.num=tree[i<<1].num+tree[i<<1|1].num;
}

void update(int i,int pos)
{
    node &p=tree[i];
    if (p.l==p.r)
    {
        p.num=0;
        return;
    }
    int mid=(p.l+p.r)>>1;
    if (pos<=mid)
        update(i<<1,pos);
    else
        update(i<<1|1,pos);
    p.num=tree[i<<1].num+tree[i<<1|1].num;
}

void search(int i)
{
    node &p=tree[i];
    if (p.l==p.r)
    {
        ans=p.l;
        return;
    }
    if (tree[i<<1].num>=now)
    {
        search(i<<1);
    }
    else
    {
        now-=tree[i<<1].num;
        search(i<<1|1);
    }
}

int main()
{
   // freopen("in.txt","r",stdin);
    while (cin>>n)
    {
        build(1,1,n);
        for (int k=1;k<=n;k++)
        {
            scanf("%d",&now);
            ans=0;
            search(1);
            a[k]=ans;
            update(1,ans);
        }
        cin>>m;
        for (int k=1;k<m;k++)
        {
            scanf("%d",&man);
            printf("%d ",a[man]);
        }
        scanf("%d",&man);
        printf("%d\n",a[man]);
    }
    return 0;
}





F

数据一直有问题。。。鄙视- -

第一次会做这种题目,记得上次简单听了算法是复旦校赛。

如果n小,并且还是有 > 和 < 的话,那么>的直接减,< 的容斥原理 + 状压dp。

如果n大,dp[i][j] 二维数组dp就可以了吧。。不过m一定要小。。。

int MM[20];
LL OP[1 << 20];
const int MMM = 1 << 20;
void init(){
    for (int i = 0 ; i < 20 ; ++i)
        MM[i] = (1 << i);
    memset(OP , 0LL , sizeof(OP));
    for (int i = 0 ; i < (MMM) ; ++ i){
        int ii = i;
        while (ii){
            if (ii & 1) OP[i] ++;
            ii>>=1;
        }
        if (OP[i] & 1LL) OP[i] = -1LL;
        else OP[i] = 1LL;
    }
}
#define typec long long
const LL modo = 100000007LL;
typec extendGCD(typec a, typec b, typec& x, typec& y)
{
    if(!b) return x = 1, y = 0, a;
    typec res = extendGCD(b, a % b, x, y), tmp = x;
    x = y, y = tmp - (a / b) * y;
    return res;
}
typec combinationModP(typec n, typec k, typec p)
{
    //if (k == 0) return 0;
    if (n < 0 || k < 0) return 0LL;
    if(k > n) return 0LL;
    if(n - k < k) k = n - k;
    typec a = 1LL, b = 1LL, x, y;
    LL pcnt = 0LL;
    for(int i = 1; i <= k; i++)
    {
        x = n - i + 1, y = i;
        while(x % p == 0) x /= p, pcnt++;
        while(y % p == 0) y /= p, pcnt--;
        x %= p, y %= p, a *= x, b *= y;
        b %= p, a %= p;
    }
    if(pcnt) return 0;
    extendGCD(b, p, x, y);
    if(x < 0) x += p;
    a *= x, a %= p;
    return a;
}
int use[MMM];
char str[1000];
char s1[150],s2[150],s3[150],s[1000];
int n , m , op;
vector<int> Meng;
int x;
void solve(){
    //cout << combinationModP(0,0,modo) << endl;
    //cout << combinationModP(2,0,modo) << endl;
    Meng.clear();
    gets(s);
    while(1)
    {
         int d;
         if(!gets(s)) break;
         if(strlen(s)<2) break;
         sscanf(s,"%s %s %s %d",s1,s2,s3,&d);
         if(s2[0]=='g') m-=(d+1);
         else Meng.push_back(d);
    }
    if (m < 0){
        printf("0\n");
        return;
    }
    int nn = (int)Meng.size();
    for (int i = 0 ; i < MM[nn] ; ++ i){
        use[i] = 0;
        int ii = i;
        for (int j = 0 ; j < nn && ii; ++ j){
            if (ii & 1) use[i] += Meng[j];
            ii >>= 1;
        }
    }
    //LL ans = combinationModP((LL)(m + n - 1) ,(LL)(n - 1) , modo);
    //cout << ans << endl;
    LL ans = 0;
    for (int i = 0 ; i < MM[nn] ; ++i){
        if (m < use[i]) continue;
        LL op = OP[i];
        LL res = combinationModP((LL)(m - use[i] + n - 1),(LL)(n - 1), modo);
        //cout << i << " "  <<res <<  endl;
        ans = (ans % modo + (op * res + modo)%modo ) % modo;
    }
    //printf("%lld\n",ans);
    printf("%lld\n",ans);
}
int main(){
    init();
    while (~scanf("%d%d",&n,&m) &&  n) solve();
}
/*
a: greater than 0
*/


H

如果那时候能冷静一点儿,不是那么浮躁的话是能想出来的。。。很水的记忆化搜索 + dp。按照题目叙述的做就成了。

const int N = 30000;
int c[N]  , n , f;
double t[N];
double dp[N];
bool vis[N];
const double MM = (sqrt(5.0) + 1.0) / 2.0;
double dfs(int nowf){
    if (vis[nowf]) return dp[nowf];
    vis[nowf] = 1;
    for (int i = 1 ; i <= n ; ++i){
        if (nowf > c[i]) dp[nowf] += t[i];
        else dp[nowf] += dfs(nowf + c[i]) + 1.0;
    }
    dp[nowf] /= (double)n;
    return dp[nowf];
}
void solve(){
    for (int i = 1 ; i <= n ; ++i){
        cin >> c[i];
        t[i] = floor(MM * sqr((double)c[i]) + 1e-8);
    }
    memset(dp , 0 , sizeof(dp));
    memset(vis , 0 , sizeof(vis));
    printf("%.3lf\n",dfs(f));
}
int main(){
    while (cin >> n >> f) solve();
}






I

并查集 + STL

记住union_set的用法

string str;
const int N = 100001;
int n , m , k , x;
set<int> info[N];
namespace ufset{
          int fa[N],rank[N];

          void init() { for (int i=0;i<N;++i) fa[i]=i,rank[i]=0; }
          int find(int x){
              int r=x,y;
              while (fa[r]!=r) r=fa[r];
              while (fa[x]!=r) { y=fa[x],fa[x]=r,x=y;}
              return r;
          }
          void unionset(int x,int y){       // x,y roots
               if (rank[x]>rank[y]) fa[y]=x;
               else { fa[x]=y; if (rank[x]==rank[y]) ++rank[y]; }
          }
};
map<string , int> name;
map<string , int> :: iterator iter;
void solve(){
    m = 0;
    name.clear();
    ufset::init();
    while (n --){
        cin >> str;
        if (str[0] == 'a'){
            cin >> str;
            int posi;
            iter = name.find(str);
            if (iter == name.end()){
                name[str] = ++m;
                info[m].clear();
                posi = m;
            }
            else posi = ufset::find(iter -> second);
            scanf("%d",&k);
            while (k--){
                scanf("%d",&x);
                info[m].insert(x);
            }
        }
        else if (str[0]=='c'){
            cin >> str;
            int posi = name[str];
            int fa = ufset::find(posi);
            printf("%d\n",info[fa].size());
        }
        else{
            int p1 , p2;
            cin >> str ; p1 = name[str];
            cin >> str ; p2 = name[str];
            p1 = ufset::find(p1);
            p2 = ufset::find(p2);
            if (ufset::find(p1) != ufset::find(p2))
                ufset::unionset(p1 , p2);
            int fa = ufset::find(p1);
            set_union(info[p1].begin() , info[p1].end() , info[p2].begin() , info[p2].end() , inserter(info[fa] , info[fa].begin()));
        }
    }
}
int main(){
    while (cin >> n) solve();
}


K

栈模拟 + KMP 。 其实不用KMP。。。

int next[1005];
stack<int> r;
char a[1005];
char s[812000];
int n,len_a,len_s;

void Get_Next()
{
    a[0] = '#';
    len_a = strlen(a);
    len_s = strlen(s);
    next[1] = 0;
    int j = 0;
    for(int i=2;i<=len_a;i++)
    {
        while(j > 0 && a[j+1] != a[i])
            j = next[j];
        if(a[j+1] == a[i])
            j++;
        next[i] = j;
    }
}
int ans;
void Str_Match()
{
    int i = 0,j = 0;
    int last = -521000;
    while(i < len_s)
    {
        while(j>0 && a[j+1]!=s[i])
            j = next[j];
        if(a[j+1] == s[i])
        {
            j++;
        }
        r.push(j);
        if(j == len_a - 1){
            j=next[j];
            ans++;
            for (int k = 1; k < len_a; ++k)
                r.pop();
            if (!r.empty())
            {
                i++;
                j = r.top();
            }else
            {
                i++;
                j = 0;
            }
        }else i++;
    }
}

int main()
{
    while(scanf("%s%s",a+1,s) != EOF)
    {
        ans = 0;
        Get_Next();
        Str_Match();
        printf("%d\n",ans);
    }
    return 0;
}


总结:

一个人单挑总是有一点儿力不从心。。。赛后AK的动力不足,这不行啊。。。

每次做题到最后没劲儿了就喜欢抱着老婆想,之后就能很踏实地搞出1~2个题目,要克服这个啊- -自己要淡定出题。

金华 + 杭州加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值