http://acm.zju.edu.cn/onlinejudge/showContestProblems.do?contestId=340
A Alice's present 题意:给出一个10^5 的数组,5w次询问,每次询问给出一段区间 [ l ,r ] 要求判断区间里的每个数是否只是出现了一次,如果是输出OK,否则输出从右 向左第一个出现两次的数
思路: 我是用线段树离线查询做的,感觉单调栈也可以做得样子,而且代码应该短很多
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define inf 0x3fffffff
const int maxn=500010;
int d[maxn<<2],first[maxn],a[maxn],tab[maxn];
struct Node
{
int l,r,id;
}q[50010];
int cmp(Node a,Node b)
{
return a.l > b.l;
}
void build(int l,int r,int rt)
{
d[rt]=inf;
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int p,int val,int l,int r,int rt)
{
if(l==r){
d[rt]=val;return;
}
int m=(l+r)>>1;
if(p<=m) update(p,val,lson);
else update(p,val,rson);
d[rt]=min(d[rt<<1],d[rt<<1|1]);
}
int query(int L,int R,int val,int l,int r,int rt)
{
if(d[rt]>val) return -1;
if(l==r) return l;
int m=(l+r)>>1;
if(R<=m) return query(L,R,val,lson);
if(L>m) return query(L,R,val,rson);
int ret2=query(m+1,R,val,rson);
if(ret2!=-1) return ret2;
return query(L,m,val,lson);;
}
int bin(int key,int l,int r)
{
while(l<=r)
{
int m=(l+r)>>1;
if(tab[m]==key) return m;
else if(tab[m]>key) r=m-1;
else l=m+1;
}
}
int ans[maxn];
int main()
{
int n,m;
while(scanf("%d",&n)==1)
{
build(1,n,1);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),tab[i-1]=a[i];
sort(tab,tab+n);
int k=unique(tab,tab+n)-tab-1;
scanf("%d",&m);
for(int i=0;i<m;i++)
scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q,q+m,cmp);
memset(first,-1,sizeof(first));
int dd=0;
for(int i=n;i>=1;i--)
{
int pos=bin(a[i],0,k);
//cout<<a[i]<<" "<<pos<<endl;
if(first[pos]==-1) first[pos]=i;
else update(i,first[pos],1,n,1),first[pos]=i;
while(dd<m&&q[dd].l==i)
{
//cout<<q[dd].l<<" "<<q[dd].r<<" ";
int p=query(q[dd].l,q[dd].r,q[dd].r,1,n,1);
ans[q[dd].id]= (p==-1)?-1:a[p];
dd++;
}
if(dd>=m) break;
}
for(int i=0;i<m;i++)
if(ans[i]!=-1) printf("%d\n",ans[i]);
else puts("OK");
puts("");
}
return 0;
}
C Cinema in Akiba 题, 线段树水题,因为输出错误害了我wa两次,无语……
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=500010;
int d[maxn<<2];
void build(int l,int r,int rt)
{
d[rt]=r-l+1;
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int update(int p,int l,int r,int rt)
{
if(l==r){
d[rt]=0;return l;
}
int m=(l+r)>>1,ret;
if(p<=d[rt<<1]) ret=update(p,lson);
else ret=update(p-d[rt<<1],rson);
d[rt]=d[rt<<1]+d[rt<<1|1];
return ret;
}
int ans[maxn];
int main()
{
int n,m,a;
while(scanf("%d",&n)==1)
{
build(1,n,1);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
ans[i]=update(a,1,n,1);
//cout<<ans[i]<<endl;
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%d",&a);
if(i)putchar(' ');
printf("%d",ans[a]);
}
puts("");
}
return 0;
}
I Information Sharing 题 Information Sharing 题意: 给出最多10w个孩子,每个孩子之间可以分享自己知道的信息,每个孩子知道的信息最多为10,信息的种类不超过1000,然后问经过一系列的之后查询某个孩子知道的信息种类数
思路: 我是用并查集做的,每个孩子都有一个集合表示该孩子知道的信息量,但是信息的种类最多是1000,孩子个数是10w,直接开数组的是开不下的,所以我用到STL的容器,把每个不是根节点的内存及时的销毁……其他的就是并查集的找父亲节点和合并两个集合就可以了
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int maxn=100010;
int fa[maxn];
vector<int> set[maxn];
map<string,int> mp;
bool flag[maxn];
int find(int a)
{
while(fa[a]!=-1) a=fa[a];
return a;
}
void Union(int u,int v)
{
int f1=find(u),f2=find(v);
if(f1==f2) return;
int sz=set[f2].size();
for(int i=0;i<sz;i++) set[f1].push_back(set[f2][i]);
sort(set[f1].begin(),set[f1].end());
set[f1].resize(unique(set[f1].begin(),set[f1].end())-set[f1].begin());
set[f2].clear();
fa[f2]=f1;
}
int main()
{
int n,m,k,a,dd;
char ord[20],name1[20],name2[20];
while(scanf("%d",&n)==1)
{
dd=1;
memset(fa,-1,sizeof(fa));
for(int i=0;i<n;i++)
{
scanf("%s",ord);
if(ord[0]=='a')
{
scanf("%s",name1);
scanf("%d",&k);
mp[name1]=dd;
set[dd].clear();
for(int j=1;j<=k;j++) scanf("%d",&a),set[dd].push_back(a);
dd++;
}
else if(ord[0]=='s')
{
scanf("%s %s",name1,name2);
int u=mp[name1],v=mp[name2];
Union(u,v);
}
else
{
scanf("%s",name1);
int f1=find(mp[name1]);
printf("%d\n",set[f1].size());
}
}
mp.clear();
}
return 0;
}
/*
8
arrive FatSheep 3 4 7 5
arrive riversouther 2 4 1
share FatSheep riversouther
check FatSheep
arrive delta 2 10 4
check delta
share delta FatSheep
check riversouther
*/
J题
Just Another Information Sharing Problem |
tick 就是 还要把m每个知道的信息 要和源点建流量为1的边,不然会wa。 代码太戳,就不贴了
K题 Keep Deleting 做得比较爽的一题,30分钟1Y。 思路就是kmp,然后建一个栈,栈里放着没有被删除的字符,从左向右扫描
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=555555;
char s[maxn],a[300],stack[maxn];
int next[300],len;
void get_next()
{
next[0]=-1;
int i=0,j=-1;
while(i<len)
{
if(j==-1||a[i]==a[j]) next[++i]=++j;
else j=next[j];
}
}
int local(int top,char c)
{
if(top==0) return (s[0]==c)?0:-1;
int i=stack[top-1]+1;
while(i!=-1&&a[i]!=c) i=next[i];
return i;
}
int solve()
{
int L=strlen(s),top=0,ret=0;
for(int i=0;i<L;i++)
{
int pos=local(top,s[i]);
if(pos>=len-1) top-=(len-1),ret++;
else stack[top++]=pos;
//cout<<s[i]<<" "<<top<<": "<<pos<<endl;
}
return ret;
}
int main(int argc, char *argv[])
{
while(scanf("%s%s",a,s)==2)
{
len=strlen(a);
get_next();
/* for(int i=0;i<len;i++)
cout<<next[i]<<" ";
cout<<endl;*/
printf("%d\n",solve());
}
return 0;
}
PS: 太弱了,其他题目都没有做出来