http://codeforces.com/problemset/problem/315/A
這兩天做了兩場CF的題(div2),沒事補個題解吧。
A題:題目很水,但是有些噁心的trick,比如有的罐子可以打開沒有出現的編號的罐子,還有自己不能打開自己,題目沒看清,WA了一堆,最後直接O(n^2)暴力水過。。。
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#define maxn 100010
using namespace std;
int a[110],b[110],num[110];
int main()
{
//freopen("dd.txt","r",stdin);
int n,i;
scanf("%d",&n);
int sum=0;
for(i=0;i<n;i++)
{
scanf("%d%d",&a[i],&b[i]);
}
for(i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(i!=j)
{
if(b[i]==a[j])
{
num[j]=1;
}
}
}
}
for(i=0;i<n;i++)
sum+=num[i];
printf("%d\n",n-sum);
return 0;
}
http://codeforces.com/problemset/problem/315/B
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 100010
#define ll long long
using namespace std;
ll a[maxn];
int main()
{
//freopen("dd.txt","r",stdin);
int n,i,m;
ll sum=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%I64d",&a[i]);
while(m--)
{
int t,x,y;
scanf("%d",&t);
{
if(t==1)
{
scanf("%d%d",&x,&y);
a[x]=y-sum;
}
else if(t==2)
{
scanf("%d",&x);
sum+=x;
}
else
{
scanf("%d",&x);
printf("%I64d\n",sum+a[x]);
}
}
}
return 0;
}
C題:題目有些長,但是看了樣例還是很容易明白的,因爲di的計算方法只和i前面的人還有人的總數n有關,且我們去掉人的順序也是從小到大,所以我們可以從左往右一個一個判定a[i]是否應該被去掉,在判斷的同時我們維護兩個值,po表示當前待判斷的人前面有多少人(沒有被去掉),nn表示當前還剩多少人。那麼對於每一個人計算di,若需要被去掉,則nn--,否則po++,如何快速計算di還是比較好想的,可以用類似dp的方法解決,具體實現請參考代碼。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <assert.h>
#define maxn 200010
#define ll long long
using namespace std;
ll a[maxn];
int vis[maxn];
int main()
{
//freopen("dd.txt","r",stdin);
int n,k;
scanf("%d%d",&n,&k);
int i,nn=n,po=1;//nn表示還剩幾人,po表示當前確定留下的人的數量
ll tmp=0,d=0;
scanf("%I64d",&a[1]);
for(i=2;i<=n;i++)
{
scanf("%I64d",&a[i]);
d=tmp-po*a[i]*(nn-po-1);//計算di
if(d<k)//說明去掉一個
{
vis[i]=1;
nn--;
}
else
{
tmp+=po*a[i];
po++;
}
}
for(i=1;i<=n;i++)
if(vis[i])
printf("%d\n",i);
return 0;
}
http://codeforces.com/problemset/problem/315/D
D題:首先很容易知道,若 [a, b] 串最多能obtain(具體含義見題目描述)x個 [c, d]串,則答案就爲x。進一步,設[a,b]最多能obtain y個c串,則x=y/d。·求y的過程類似於求循環節,設num[i]表示[a,i]能obtain多少個c串,且設l2[i]表示[a,i]匹配完後最後一個匹配到c串的第幾個字符,然後求循環節即可,具體實現還需要些細節,不嫌代碼醜的話可以參考代碼:
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 100010
using namespace std;
char s1[110],s2[110];
int num[10100],vis[110];
int main()
{
//freopen("dd.txt","r",stdin);
int b,d;
scanf("%d%d",&b,&d);
scanf("%s%s",s1,s2);
int len1=strlen(s1),len2=strlen(s2);
int t=0,l1=0,l2=0,sum=0,old=0;
num[0]=0;
int v[26];
memset(v,0,sizeof(v));
for(int i=0;i<len1;i++)
v[s1[i]-'a']++;
for(int i=0;i<len2;i++)
{
if(!v[s2[i]-'a'])
{
printf("0\n");
return 0;
}
}
memset(vis,-1,sizeof(vis));
vis[0]=0;
while(1)
{
if(s1[l1]==s2[l2])
{
l1++;
l2++;
}
else
l1++;
if(l2==len2)
{
sum++;
l2=0;
}
if(l1>len1)
{
t++;
num[t]=sum;
l1=0;
if(vis[l2]!=-1)
{
old=vis[l2];
break;
}
vis[l2]=t;
}
}
int tmp=0;
if(b<=t)
{
tmp=num[b];
}
else
{
tmp=num[t]+(num[t]-num[old])*((b-t)/(t-old));
b-=t;
b%=(t-old);
tmp+=num[old+b]-num[old];
}
printf("%d\n",tmp/d);
return 0;
}
E題:很水的dp,易知我們要求的就是a串的所有不同的非下降子序列中,各位數乘積的和,我們設dp[i]表示以i結尾的非下降子序列的答案,我們從左到右一步一步更新dp值,轉移方程很簡單,爲: dp[i]=(dp[1]+dp[2]+……dp[i])*i+i,簡單點表述就是我在之前求得的以x(x<=i)結尾的非下降子序列後加上一個i,然後再加上i自身,這樣可以保證序列不重複,至於爲什麼在紙上畫畫就知道了,直接這麼做是不行的,顯然會超時,區間和還有單點更新可以用線段樹或者樹狀數組維護,以下是樹狀數組的實現。
#include <iostream>
#include <stdio.h>
#include <string.h>
#define ll long long
#define maxn 1000010
#define mod 1000000007
using namespace std;
ll dp[maxn],c[maxn];
int nn=1000000;
int lowbit(int x)
{
return x&(-x);
}
void add(int x,ll val)
{
while(x<=nn)
{
c[x]=(c[x]+val)%mod;
x+=lowbit(x);
}
}
ll getsum(int x)
{
ll sum=0;
while(x>0)
{
sum=(c[x]+sum)%mod;
x-=lowbit(x);
}
return sum;
}
int main()
{
//freopen("dd.txt","r",stdin);
int i,n,x;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
ll tmp=dp[x];
dp[x]=(getsum(x)*x+x)%mod;
add(x,((dp[x]-tmp)%mod+mod)%mod);
}
ll ans=0;
for(i=1;i<=1000000;i++)
{
ans+=dp[i];
}
ans%=mod;
cout<<ans<<endl;
return 0;
}