2015-2016 Petrozavodsk Winter Training Camp, Nizhny Novgorod SU Contest (5/9)

2015-2016 Petrozavodsk Winter Training Camp, Nizhny Novgorod SU Contest

B. Forcefield

题意

给你一维平面上n个镜子,镜子的朝向有正面和背面,如果光束从正面穿过,会摧毁镜子,并且光束会反向;如果从背面穿过的话,什么都不会发生。

光束一开始从X位置,射向0点,然后你人在0点,会反射光束。

问你要破坏所有镜子,人需要反弹光束多少次。

数据范围100000

题解

其实模拟就好了,击破镜子的顺序就那么一种。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+6;
const int inf = 1e9+1;
set<int> s1,s2;
int n,x[maxn],p[maxn];
set<int>::iterator it;
int main()
{
    scanf("%d%d",&n,&x[0]);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x[i],&p[i]);
        if(p[i]==1)s1.insert(x[i]);
        if(p[i]==0)s2.insert(x[i]);
    }
    s2.insert(0);
    s1.insert(0);
    s2.insert(inf);
    s1.insert(inf);
    int now = x[0],flag=2;
    int ans = 0;
    while(1)
    {
        if(flag==2){
            int pos = *--s2.lower_bound(now);
            if(pos==0){
                ans++;
                now=pos;
            }
            else{
                s2.erase(pos);
                now=pos;
            }
            flag=1;
        }else{
            int pos = *s1.upper_bound(now);
            if(pos==inf){
                if(s1.size()==2&&s2.size()==2)
                    break;
                printf("-1
");
                return 0;
            }else{
                s1.erase(pos);
                now=pos;
            }
            flag=2;
        }
    }
    cout<<ans<<endl;
}

C.Missing Part

题意

给你一个串S,这个S是环状的,给你另外一个字符串S1。

给你大写的ABCDE和小写的abcde,然后你需要定义一种对应关系,使得失配数最小。

题解

假设只有两种字符,那么就是FFT了。

然后这个是5个嘛,最粗鲁的做法是120个FFT。

优化一下 就是25个FFT就好了。

队友写的,实验细节不知道。

代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5e4 + 50;
char s1[maxn << 1],s2[maxn];
int n ,ans,f[15],use[15];
vector < int > pos[5] , ano[5];

const double PI = acos(-1.0);
//复数结构体
struct Complex
{
    double r,i;
    Complex(double _r = 0.0,double _i = 0.0)
    {
        r = _r; i = _i;
    }
    Complex operator +(const Complex &b)
    {
        return Complex(r+b.r,i+b.i);
    }
    Complex operator -(const Complex &b)
    {
        return Complex(r-b.r,i-b.i);
    }
    Complex operator *(const Complex &b)
    {
        return Complex(r*b.r-i*b.i,r*b.i+i*b.r);
    }
};
/*
 * 进行FFT和IFFT前的反转变换。
 * 位置i和 (i二进制反转后位置)互换
 * len必须去2的幂
 */
void change(Complex y[],int len)
{
    int i,j,k;
    for(i = 1, j = len/2;i < len-1; i++)
    {
        if(i < j)swap(y[i],y[j]);
        //交换互为小标反转的元素,i<j保证交换一次
        //i做正常的+1,j左反转类型的+1,始终保持i和j是反转的
        k = len/2;
        while( j >= k)
        {
            j -= k;
            k /= 2;
        }
        if(j < k) j += k;
    }
}
/*
 * 做FFT
 * len必须为2^k形式,
 * on==1时是DFT,on==-1时是IDFT
 */
void fft(Complex y[],int len,int on)
{
    change(y,len);
    for(int h = 2; h <= len; h <<= 1)
    {
        Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
        for(int j = 0;j < len;j+=h)
        {
            Complex w(1,0);
            for(int k = j;k < j+h/2;k++)
            {
                Complex u = y[k];
                Complex t = w*y[k+h/2];
                y[k] = u+t;
                y[k+h/2] = u-t;
                w = w*wn;
            }
        }
    }
    if(on == -1)
        for(int i = 0;i < len;i++)
            y[i].r /= len;
}

int len = 131072;

Complex X[131075],Y[131075];

int ret[5][5][maxn];

void predeal(){
    for(int i = 0 ; i < 5 ; ++ i)
		for(int j = 0 ; j < 5 ; ++ j){
			memset( X , 0 , sizeof( X ) );
			memset( Y , 0 , sizeof( Y ) );
			int x = i , y = j;
			for(auto it : pos[i]) X[it+1].r++;
			for(auto it : ano[y]) Y[n-it].r++;
			fft( X , len , 1 );
			fft( Y , len , 1 );
			for(int j = 0 ; j < len ; ++ j) X[j] = X[j] * Y[j];
			fft( X , len , -1 );
			for(int k = n + 1 ; k <= 2 * n ; ++ k) ret[i][j][k - n] += (int)(X[k].r + 0.5);
		}
}

void cal_ans(){
	for(int i = 1 ; i <= n ; ++ i){
		int sum = 0;
		for(int j = 0 ; j < 5 ; ++ j)
			sum += ret[j][f[j]][i];
		ans = max( ans , sum );
	}
}

void DFS( int x ){
	if( x == 5 ){
		cal_ans();
	}else{
		for(int i = 0 ; i < 5 ; ++ i)
			if(!use[i]){
				f[x] = i;
				use[i] = 1;
				DFS( x + 1 );
				use[i] = 0;
			}
	}
}

int main(int argc , char * argv[] ){
	freopen("in.txt","r",stdin);
	scanf("%s%s",s1,s2);
	n=strlen(s1);
	for(int i = 0 ; i < n ; ++ i) s1[i + n] = s1[i];
	for(int i = 0 ; i < (n * 2) ; ++ i) pos[s1[i] - 'A'].push_back( i );
	for(int i = 0 ; i < n ; ++ i) ano[s2[i] - 'a'].push_back( i );
	predeal();
	DFS( 0 );
	printf("%d
",n-ans);
	return 0;
}

The Jedi Killer

题意

平面上给你三个点,然后给你三条线段的长度,两条为lg,一条为lm。

三条线段的一个端点都在同一个位置,且lg垂直于lm。

问你这三个线段是否能够覆盖题目所给的三个点。

题解

丝薄题,XJB判一判就好了。

就三种情况,lm穿过两个点,lg穿过两个点,或者三点共线。

判判判就好了。

代码

#include<bits/stdc++.h>

using namespace std;

struct point
{
    long long x,y;
}p[3];

long long cross(point p1,point p2,point p0)
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
long long dist2(point p1,point p2)
{
    return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}

void solve()
{
    long long lg,lm;
    cin>>lm>>lg;
    for(int i=0;i<3;i++)
        cin>>p[i].x>>p[i].y;
    if(cross(p[0],p[1],p[2])==0)
    {
        long long tmp=max(lm*lm,lg*lg*4LL);
        if(dist2(p[0],p[1])<=tmp&&dist2(p[1],p[2])<=tmp&&dist2(p[2],p[0])<=tmp)
            printf("YES
");
        else
            printf("NO
");
        return ;
    }
    for(int i=0;i<3;i++)
    {
        for(int j=i+1;j<3;j++)
        {
            for(int k=0;k<3;k++)
                if(k!=i&&k!=j)
                {
                    int flag=1;
                    long long a=p[i].y-p[j].y,b=p[j].x-p[i].x;
                    long long c=-a*p[i].x-b*p[i].y;
                    long long tmp=(a*p[k].x+b*p[k].y+c)*(a*p[k].x+b*p[k].y+c);
                    if(lm*lm*(a*a+b*b)<tmp) flag=0;
                    if(dist2(p[i],p[k])*(a*a+b*b)-tmp>lg*lg*(a*a+b*b)) flag=0;
                    if(dist2(p[j],p[k])*(a*a+b*b)-tmp>lg*lg*(a*a+b*b)) flag=0;
                    if(flag)
                    {
                        printf("YES
");
                        return ;
                    }
                    flag=1;
                    if((dist2(p[i],p[j])+dist2(p[i],p[k])-dist2(p[j],p[k]))>0&&(dist2(p[i],p[j])+dist2(p[j],p[k])-dist2(p[i],p[k]))>0) flag=0;
                    if(lg*lg*(a*a+b*b)<tmp) flag=0;
                    if(dist2(p[i],p[k])*(a*a+b*b)-tmp>lm*lm*(a*a+b*b)) flag=0;
                    if(dist2(p[j],p[k])*(a*a+b*b)-tmp>lm*lm*(a*a+b*b)) flag=0;
                    if(flag)
                    {
                        printf("YES
");
                        return ;
                    }
                }
        }
    }
    printf("NO
");
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)solve();
    return 0;
}

G. Youngling Tournament

题意

给你n个数,然后从大到小排序,如果这个数不小于他后面的数的和,那么这个数就是胜利者。

单点修改,问你每次胜利者有多少个。

题解

其实就是问你f[i]+sum[i]>=sum[n]的有多少个。

实际上就是区间修改+单点修改+查询区间第k大。

分块莽莽莽,然后再调一调常数就好了。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
inline long long read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,N,m,num,belong[maxn],x[maxn],block,l[maxn],r[maxn],flag[maxn],p[maxn];
long long a[maxn],val[maxn],c[maxn];
long long sum[maxn],B[2000];
long long d[maxn];
map<long long,int> H;
int use[maxn],cnt;
vector<int>E[maxn];
int getid(long long x)
{
    if(H[x])return H[x];
    if(!H[x])H[x]=++cnt;
    return H[x];
}
vector<long long> V;
void build()
{
    N=V.size();
    block=sqrt(N/2);
    num=N/block;if(N%block)num++;
    for(int i=1;i<=num;i++)
        l[i]=(i-1)*block+1,r[i]=i*block;
    r[num]=N;
    for(int i=1;i<=N;i++)
        belong[i]=(i-1)/block+1;
    int tot = 1;
    for(int i=1;i<=N;i++)
        E[getid(a[i])].push_back(i);
    for(int i=1;i<=n;i++)
        flag[E[getid(c[i])][use[getid(c[i])]++]]=1;
}
void build(int l,int r)
{
    int id=belong[l];B[id]=0;
    for(int i=l;i<=r;i++)d[i]=0,sum[i]=0;
    for(int i=l;i<=r;i++)
    {
        if(i!=l)sum[i]=sum[i-1];
        if(flag[i]==1){
            sum[i]+=a[i];
            B[id]+=a[i];
            d[i]=a[i]+sum[i];
        }else{
            d[i]=-1e9;
        }
    }
    sort(d+l,d+r+1);
}
void query()
{
    long long Sum = 0;
    for(int i=1;i<=num;i++)
        Sum+=B[i];
    long long tmp = 0;
    int ans = 0;
    for(int i=1;i<=num;i++)
    {
        int L=l[i],R=r[i],Ans=r[i]+1;
        while(L<=R)
        {
            int mid=(L+R)/2;
            if(d[mid]+tmp>=Sum)Ans=mid,R=mid-1;
            else L=mid+1;
        }
        ans+=(r[i]-Ans+1);
        tmp+=B[i];
    }
    printf("%d
",ans);
}
int main()
{
    //freopen("1.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        a[i]=read();c[i]=a[i];
        V.push_back(a[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        p[i]=read();val[i]=read();
        V.push_back(val[i]);
    }
    sort(V.begin(),V.end());
    reverse(V.begin(),V.end());
    for(int i=0;i<V.size();i++)
        a[i+1]=V[i];
    build();
    for(int i=1;i<=num;i++)
        build(l[i],r[i]);
    query();
    for(int i=1;i<=m;i++)
    {
        int pos = E[getid(c[p[i]])][--use[getid(c[p[i]])]];
        flag[pos]=0;
        build(l[belong[pos]],r[belong[pos]]);
        c[p[i]]=val[i];
        pos=E[getid(c[p[i]])][use[getid(c[p[i]])]++];
        flag[pos]=1;
        build(l[belong[pos]],r[belong[pos]]);
        query();
    }
}

H. Garland Checking

题意

强制在线,给你一棵树

三个操作:

连接a,b

切掉a,b

查询a,b之间是否有边。

题解

裸LCT

代码

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

const int MAXN = 200010;
struct Node {
    Node *ch[2], *p; int size, value;
    bool rev;
    Node(int t = 0);
    inline bool dir(void) {return p->ch[1] == this;}
    inline void SetC(Node *x, bool d) {
        ch[d] = x; x->p = this;
    }
    inline void Rev(void) {
        swap(ch[0], ch[1]); rev ^= 1;
    }
    inline void Push(void) {
        if (rev) {
            ch[0]->Rev();
            ch[1]->Rev();
            rev = 0;
        }
    }
    inline void Update(void) {
        size = ch[0]->size + ch[1]->size + 1;
    }
}Tnull, *null = &Tnull, *fim[MAXN];
// 要记得额外更新null的信息
Node::Node(int _value){ch[0] = ch[1] = p = null; rev = 0;}
inline bool isRoot(Node *x) {return x->p == null || (x != x->p->ch[0] && x != x->p->ch[1]);}
inline void rotate(Node *x) {
    Node *p = x->p; bool d = x->dir();
    p->Push(); x->Push();
    if (!isRoot(p)) p->p->SetC(x, p->dir()); else x->p = p->p;
    p->SetC(x->ch[!d], d);
    x->SetC(p, !d);
    p->Update();
}
inline void splay(Node *x) {
    x->Push();
    while (!isRoot(x)) {
        if (isRoot(x->p)) rotate(x);
        else {
            if (x->dir() == x->p->dir()) {rotate(x->p); rotate(x);}
            else {rotate(x); rotate(x);}
        }
    }
    x->Update();
}
inline Node* Access(Node *x) {
    Node *t = x, *q = null;
    for (; x != null; x = x->p) {
        splay(x); x->ch[1] = q; q = x;
    }
    splay(t); //info will be updated in the splay;
    return q;
}
inline void Evert(Node *x) {
    Access(x); x->Rev();
}
inline void link(Node *x, Node *y) {
    Evert(x); x->p = y;
}
inline Node* getRoot(Node *x) {
    Node *tmp = x;
    Access(x);
    while (tmp->Push(), tmp->ch[0] != null) tmp = tmp->ch[0];
    splay(tmp);
    return tmp;
}
// 一定要确定x和y之间有边
inline void cut(Node *x, Node *y) {
    Access(x); splay(y);
    if (y->p != x) swap(x, y);
    Access(x); splay(y);
    y->p = null;
}
inline Node* getPath(Node *x, Node *y) {
    Evert(x); Access(y);
    return y;
}
inline void clear(void) {
    null->rev = 0; null->size = 0; null->value = 0;
}

int judge(Node *x,Node *y)
{
    while(x->p!=null)x = x->p;
    while(y->p!=null)y = y->p;
    if(x!=y)return 0;
    return 1;
}
int main()
{
    clear();
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        fim[i] = new Node();
    string c;
    int u,v;
    while(1)
    {
        cin>>c;
        if(c[0]=='E')break;
        cin>>u>>v;
        if(c[0]=='T')
        {
            if(judge(fim[u],fim[v]))
                cout<<"YES"<<endl;
            else
                cout<<"NO"<<endl;
        }
        else if(c[0]=='C')
            link(fim[u],fim[v]);
        else
            cut(fim[u],fim[v]);
    }
}
原文地址:https://www.cnblogs.com/qscqesze/p/6022143.html